]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-auth: Merge test-auth-master and test-auth-master2
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 3 May 2021 16:16:04 +0000 (19:16 +0300)
committertimo.sirainen <timo.sirainen@open-xchange.com>
Tue, 28 Jun 2022 08:10:53 +0000 (08:10 +0000)
src/lib-auth/Makefile.am
src/lib-auth/test-auth-master.c
src/lib-auth/test-auth-master2.c [deleted file]

index 5bf805c27669c15aa6143aad5983d4e6fa74a378..c1515532a233416fae50686d64851c3c99fec840 100644 (file)
@@ -25,8 +25,7 @@ pkginc_lib_HEADERS = $(headers)
 
 test_programs = \
        test-auth-client \
-       test-auth-master \
-       test-auth-master2
+       test-auth-master
 
 noinst_PROGRAMS = $(test_programs)
 
@@ -51,10 +50,6 @@ test_auth_client_SOURCES = test-auth-client.c
 test_auth_client_LDADD = $(test_libs)
 test_auth_client_DEPENDENCIES = $(test_deps)
 
-test_auth_master2_SOURCES = test-auth-master2.c
-test_auth_master2_LDADD = $(test_libs)
-test_auth_master2_DEPENDENCIES = $(test_deps)
-
 check-local:
        for bin in $(test_programs); do \
          if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \
index 95c9dafe712415257914defe0a17dc14cb1f3bee..e2331bfedbb82254c244c5299588798649e4a406 100644 (file)
@@ -1,10 +1,87 @@
-/* Copyright (c) 2019 Dovecot authors, see the included COPYING file */
+/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
-#include "auth-master.h"
-#include "net.h"
-#include "test-common.h"
 #include "str.h"
+#include "strescape.h"
+#include "hostpid.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "istream-chain.h"
+#include "ostream.h"
+#include "time-util.h"
+#include "sleep.h"
+#include "unlink-directory.h"
+#include "write-full.h"
+#include "connection.h"
+#include "master-interface.h"
+#include "test-common.h"
+#include "test-subprocess.h"
+
+#include "auth-master.h"
+
+#include <unistd.h>
+
+#define TEST_SOCKET "./auth-master-test"
+#define SERVER_KILL_TIMEOUT_SECS    20
+
+static void main_deinit(void);
+
+/*
+ * Types
+ */
+
+struct server_connection {
+       struct connection conn;
+
+       void *context;
+
+       pool_t pool;
+};
+
+typedef void test_server_init_t(void);
+typedef bool test_client_init_t(void);
+
+/*
+ * State
+ */
+
+/* common */
+static struct ioloop *ioloop;
+static bool debug = FALSE;
+
+/* server */
+static struct io *io_listen;
+static int fd_listen = -1;
+static struct connection_list *server_conn_list;
+static void (*test_server_input)(struct server_connection *conn);
+static void (*test_server_init)(struct server_connection *conn);
+static void (*test_server_deinit)(struct server_connection *conn);
+
+/* client */
+
+/*
+ * Forward declarations
+ */
+
+/* server */
+static void test_server_run(void);
+static void server_connection_deinit(struct server_connection **_conn);
+
+/* client */
+static void test_client_deinit(void);
+
+static int
+test_client_passdb_lookup_simple(const char *user, bool retry,
+                                const char **error_r);
+static int
+test_client_userdb_lookup_simple(const char *user, bool retry,
+                                const char **error_r);
+static int test_client_user_list_simple(void);
+
+/* test*/
+static void
+test_run_client_server(test_client_init_t *client_test,
+                      test_server_init_t *server_test) ATTR_NULL(2);
 
 static void test_auth_user_info_export(void)
 {
@@ -64,11 +141,1302 @@ static void test_auth_user_info_export(void)
        test_end();
 }
 
-int main(void)
+/*
+ * Connection refused
+ */
+
+/* server */
+
+static void test_server_connection_refused(void)
+{
+       i_close_fd(&fd_listen);
+       i_sleep_intr_secs(500);
+}
+
+/* client */
+
+static bool test_client_connection_refused(void)
+{
+       const char *error;
+       int ret;
+
+       ret = test_client_passdb_lookup_simple("harrie", FALSE, &error);
+       test_out_reason("run (ret == -1)", ret == -1, error);
+
+       return FALSE;
+}
+
+/* test */
+
+static void test_connection_refused(void)
+{
+       test_begin("connection refused");
+       test_expect_error_string("Connection refused");
+       test_run_client_server(test_client_connection_refused,
+                              test_server_connection_refused);
+       test_end();
+}
+
+/*
+ * Connection timed out
+ */
+
+/* server */
+
+static void test_connection_timed_out_input(struct server_connection *conn)
+{
+       i_sleep_intr_secs(5);
+       server_connection_deinit(&conn);
+}
+
+static void test_server_connection_timed_out(void)
+{
+       test_server_input = test_connection_timed_out_input;
+       test_server_run();
+}
+
+/* client */
+
+static bool test_client_connection_timed_out(void)
+{
+       time_t time;
+       const char *error;
+       int ret;
+
+       io_loop_time_refresh();
+       time = ioloop_time;
+
+       ret = test_client_passdb_lookup_simple("harrie", FALSE, &error);
+       test_out_reason("run (ret == -1)", ret == -1, error);
+
+       io_loop_time_refresh();
+       test_out("timeout", (ioloop_time - time) < 5);
+       return FALSE;
+}
+
+/* test */
+
+static void test_connection_timed_out(void)
+{
+       test_begin("connection timed out");
+       test_expect_error_string("Connecting timed out");
+       test_run_client_server(test_client_connection_timed_out,
+                              test_server_connection_timed_out);
+       test_end();
+}
+
+/*
+ * Bad VERSION
+ */
+
+/* server */
+
+static void test_bad_version_input(struct server_connection *conn)
+{
+       server_connection_deinit(&conn);
+}
+
+static void test_bad_version_init(struct server_connection *conn)
+{
+       o_stream_nsend_str(conn->conn.output, "VERSION\t666\t666\n");
+       o_stream_nsend_str(conn->conn.output, "SPID\t23234\n");
+}
+
+static void test_server_bad_version(void)
+{
+       test_server_init = test_bad_version_init;
+       test_server_input = test_bad_version_input;
+       test_server_run();
+}
+
+/* client */
+
+static bool test_client_bad_version(void)
+{
+       const char *error;
+       int ret;
+
+       ret = test_client_passdb_lookup_simple("harrie", FALSE, &error);
+       test_out_reason("run (ret == -1)", ret == -1, error);
+       return FALSE;
+}
+
+/* test */
+
+static void test_bad_version(void)
+{
+       test_begin("bad version");
+       test_expect_error_string("Socket supports major version 666");
+       test_run_client_server(test_client_bad_version,
+                              test_server_bad_version);
+       test_end();
+}
+
+/*
+ * Disconnect VERSION
+ */
+
+/* server */
+
+static void test_disconnect_version_input(struct server_connection *conn)
+{
+       const char *line;
+
+       line = i_stream_read_next_line(conn->conn.input);
+       if (line == NULL) {
+               if (conn->conn.input->eof)
+                       server_connection_deinit(&conn);
+               return;
+       }
+       server_connection_deinit(&conn);
+}
+
+static void test_disconnect_version_init(struct server_connection *conn)
+{
+       o_stream_nsend_str(conn->conn.output, "VERSION\t1\t0\n");
+       o_stream_nsend_str(conn->conn.output, "SPID\t23234\n");
+}
+
+static void test_server_disconnect_version(void)
+{
+       test_server_init = test_disconnect_version_init;
+       test_server_input = test_disconnect_version_input;
+       test_server_run();
+}
+
+/* client */
+
+static bool test_client_disconnect_version(void)
+{
+       const char *error;
+       int ret;
+
+       ret = test_client_passdb_lookup_simple("harrie", FALSE, &error);
+       test_out_reason("run (ret == -1)", ret == -1, error);
+       return FALSE;
+}
+
+/* test */
+
+static void test_disconnect_version(void)
+{
+       test_begin("disconnect version");
+       test_expect_error_string("Disconnected unexpectedly");
+       test_run_client_server(test_client_disconnect_version,
+                              test_server_disconnect_version);
+       test_end();
+}
+
+/*
+ * Passdb FAIL
+ */
+
+/* server */
+
+enum _passdb_fail_state {
+       PASSDB_FAIL_STATE_VERSION = 0,
+       PASSDB_FAIL_STATE_PASS
+};
+
+struct _passdb_fail_server {
+       enum _passdb_fail_state state;
+
+       bool not_found:1;
+};
+
+static void test_passdb_fail_input(struct server_connection *conn)
+{
+       struct _passdb_fail_server *ctx =
+               (struct _passdb_fail_server *)conn->context;
+       const char *const *args;
+       unsigned int id;
+       const char *line;
+
+       for (;;) {
+               line = i_stream_read_next_line(conn->conn.input);
+               if (line == NULL) {
+                       if (conn->conn.input->eof)
+                               server_connection_deinit(&conn);
+                       return;
+               }
+
+               switch (ctx->state) {
+               case PASSDB_FAIL_STATE_VERSION:
+                       if (!str_begins_with(line, "VERSION\t")) {
+                               i_error("Bad VERSION");
+                               server_connection_deinit(&conn);
+                               return;
+                       }
+                       ctx->state = PASSDB_FAIL_STATE_PASS;
+                       continue;
+               case PASSDB_FAIL_STATE_PASS:
+                       args = t_strsplit_tabescaped(line);
+                       if (strcmp(args[0], "PASS") != 0 || args[1] == NULL ||
+                           str_to_uint(args[1], &id) < 0 || args[2] == NULL) {
+                               i_error("Bad PASS request");
+                               server_connection_deinit(&conn);
+                               return;
+                       }
+                       if (strcmp(args[2], "henk") == 0) {
+                               line = t_strdup_printf("NOTFOUND\t%u\n", id);
+                       } else if (strcmp(args[2], "holger") == 0) {
+                               i_sleep_intr_secs(5);
+                               server_connection_deinit(&conn);
+                               return;
+                       } else if (strcmp(args[2], "hendrik") == 0) {
+                               server_connection_deinit(&conn);
+                               return;
+                       } else {
+                               line = t_strdup_printf(
+                                       "FAIL\t%u\t"
+                                       "reason=You shall not pass!!\n", id);
+                       }
+                       o_stream_nsend_str(conn->conn.output, line);
+                       server_connection_deinit(&conn);
+                       return;
+               }
+               i_unreached();
+       }
+}
+
+static void test_passdb_fail_init(struct server_connection *conn)
+{
+       struct _passdb_fail_server *ctx;
+
+       ctx = p_new(conn->pool, struct _passdb_fail_server, 1);
+       conn->context = (void*)ctx;
+
+       o_stream_nsend_str(conn->conn.output, "VERSION\t1\t0\n");
+       o_stream_nsend_str(conn->conn.output, "SPID\t23234\n");
+}
+
+static void test_server_passdb_fail(void)
+{
+       test_server_init = test_passdb_fail_init;
+       test_server_input = test_passdb_fail_input;
+       test_server_run();
+}
+
+/* client */
+
+static bool test_client_passdb_fail(void)
+{
+       const char *error;
+       int ret;
+
+       ret = test_client_passdb_lookup_simple("harrie", FALSE, &error);
+       test_out("run (ret == -2)", ret == -2);
+       test_assert(error != NULL &&
+                   strcmp(error, "You shall not pass!!") == 0);
+
+       return FALSE;
+}
+
+static bool test_client_passdb_notfound(void)
+{
+       const char *error;
+       int ret;
+
+       ret = test_client_passdb_lookup_simple("henk", FALSE, &error);
+       test_out("run (ret == 0)", ret == 0);
+       test_assert(error == NULL);
+
+       return FALSE;
+}
+
+static bool test_client_passdb_timeout(void)
+{
+       const char *error;
+       int ret;
+
+       ret = test_client_passdb_lookup_simple("holger", FALSE, &error);
+       test_out("run (ret == -1)", ret == -1);
+       test_assert(error == NULL);
+
+       return FALSE;
+}
+
+static bool test_client_passdb_disconnect(void)
+{
+       const char *error;
+       int ret;
+
+       ret = test_client_passdb_lookup_simple("hendrik", FALSE, &error);
+       test_out("run (ret == -1)", ret == -1);
+       test_assert(error == NULL);
+
+       return FALSE;
+}
+
+static bool test_client_passdb_reconnect(void)
+{
+       const char *error;
+       int ret;
+
+       ret = test_client_passdb_lookup_simple("hendrik", TRUE, &error);
+       test_out("run (ret == -1)", ret == -1);
+       test_assert(error == NULL);
+
+       return FALSE;
+}
+
+/* test */
+
+static void test_passdb_fail(void)
+{
+       test_begin("passdb fail");
+       test_run_client_server(test_client_passdb_fail,
+                              test_server_passdb_fail);
+       test_end();
+
+       test_begin("passdb notfound");
+       test_run_client_server(test_client_passdb_notfound,
+                              test_server_passdb_fail);
+       test_end();
+
+       test_begin("passdb timeout");
+       test_expect_error_string("Request timed out");
+       test_run_client_server(test_client_passdb_timeout,
+                              test_server_passdb_fail);
+       test_end();
+
+       test_begin("passdb disconnect");
+       test_expect_error_string("Disconnected unexpectedly");
+       test_run_client_server(test_client_passdb_disconnect,
+                              test_server_passdb_fail);
+       test_end();
+
+       test_begin("passdb reconnect");
+       test_expect_errors(2);
+       test_run_client_server(test_client_passdb_reconnect,
+                              test_server_passdb_fail);
+       test_end();
+}
+
+/*
+ * Userdb FAIL
+ */
+
+/* server */
+
+enum _userdb_fail_state {
+       USERDB_FAIL_STATE_VERSION = 0,
+       USERDB_FAIL_STATE_USER
+};
+
+struct _userdb_fail_server {
+       enum _userdb_fail_state state;
+
+       bool not_found:1;
+};
+
+static void test_userdb_fail_input(struct server_connection *conn)
+{
+       struct _userdb_fail_server *ctx =
+               (struct _userdb_fail_server *)conn->context;
+       const char *const *args;
+       unsigned int id;
+       const char *line;
+
+       for (;;) {
+               line = i_stream_read_next_line(conn->conn.input);
+               if (line == NULL) {
+                       if (conn->conn.input->eof)
+                               server_connection_deinit(&conn);
+                       return;
+               }
+
+               switch (ctx->state) {
+               case USERDB_FAIL_STATE_VERSION:
+                       if (!str_begins_with(line, "VERSION\t")) {
+                               i_error("Bad VERSION");
+                               server_connection_deinit(&conn);
+                               return;
+                       }
+                       ctx->state = USERDB_FAIL_STATE_USER;
+                       continue;
+               case USERDB_FAIL_STATE_USER:
+                       args = t_strsplit_tabescaped(line);
+                       if (strcmp(args[0], "USER") != 0 || args[1] == NULL ||
+                           str_to_uint(args[1], &id) < 0) {
+                               i_error("Bad USER request");
+                               server_connection_deinit(&conn);
+                               return;
+                       }
+                       if (strcmp(args[2], "henk") == 0) {
+                               line = t_strdup_printf("NOTFOUND\t%u\n", id);
+                       } else if (strcmp(args[2], "holger") == 0) {
+                               i_sleep_intr_secs(5);
+                               server_connection_deinit(&conn);
+                               return;
+                       } else if (strcmp(args[2], "hendrik") == 0) {
+                               server_connection_deinit(&conn);
+                               return;
+                       } else {
+                               line = t_strdup_printf("FAIL\t%u\t"
+                                       "reason=It is no use!\n", id);
+                       }
+                       o_stream_nsend_str(conn->conn.output, line);
+                       server_connection_deinit(&conn);
+                       return;
+               }
+               i_unreached();
+       }
+}
+
+static void test_userdb_fail_init(struct server_connection *conn)
+{
+       struct _userdb_fail_server *ctx;
+
+       ctx = p_new(conn->pool, struct _userdb_fail_server, 1);
+       conn->context = (void*)ctx;
+
+       o_stream_nsend_str(conn->conn.output, "VERSION\t1\t0\n");
+       o_stream_nsend_str(conn->conn.output, "SPID\t23234\n");
+}
+
+static void test_server_userdb_fail(void)
+{
+       test_server_init = test_userdb_fail_init;
+       test_server_input = test_userdb_fail_input;
+       test_server_run();
+}
+
+/* client */
+
+static bool test_client_userdb_fail(void)
+{
+       const char *error;
+       int ret;
+
+       ret = test_client_userdb_lookup_simple("harrie", FALSE, &error);
+       test_out("run (ret == -2)", ret == -2);
+       test_assert(error != NULL &&
+                   strcmp(error, "It is no use!") == 0);
+
+       return FALSE;
+}
+
+static bool test_client_userdb_notfound(void)
+{
+       const char *error;
+       int ret;
+
+       ret = test_client_userdb_lookup_simple("henk", FALSE, &error);
+       test_out("run (ret == 0)", ret == 0);
+       test_assert(error == NULL);
+
+       return FALSE;
+}
+
+static bool test_client_userdb_timeout(void)
+{
+       const char *error;
+       int ret;
+
+       ret = test_client_userdb_lookup_simple("holger", FALSE, &error);
+       test_out("run (ret == -1)", ret == -1);
+       test_assert(error == NULL);
+
+       return FALSE;
+}
+
+static bool test_client_userdb_disconnect(void)
+{
+       const char *error;
+       int ret;
+
+       ret = test_client_userdb_lookup_simple("hendrik", FALSE, &error);
+       test_out("run (ret == -1)", ret == -1);
+       test_assert(error == NULL);
+
+       return FALSE;
+}
+
+static bool test_client_userdb_reconnect(void)
+{
+       const char *error;
+       int ret;
+
+       ret = test_client_userdb_lookup_simple("hendrik", TRUE, &error);
+       test_out("run (ret == -1)", ret == -1);
+       test_assert(error == NULL);
+
+       return FALSE;
+}
+
+/* test */
+
+static void test_userdb_fail(void)
+{
+       test_begin("userdb fail");
+       test_run_client_server(test_client_userdb_fail,
+                              test_server_userdb_fail);
+       test_end();
+
+       test_begin("userdb notfound");
+       test_run_client_server(test_client_userdb_notfound,
+                              test_server_userdb_fail);
+       test_end();
+
+       test_begin("userdb timeout");
+       test_expect_error_string("Request timed out");
+       test_run_client_server(test_client_userdb_timeout,
+                              test_server_userdb_fail);
+       test_end();
+
+       test_begin("userdb disconnect");
+       test_expect_error_string("Disconnected unexpectedly");
+       test_run_client_server(test_client_userdb_disconnect,
+                              test_server_userdb_fail);
+       test_end();
+
+       test_begin("userdb reconnect");
+       test_expect_errors(2);
+       test_run_client_server(test_client_userdb_reconnect,
+                              test_server_userdb_fail);
+       test_end();
+}
+
+/*
+ * User list FAIL
+ */
+
+/* server */
+
+enum _user_list_fail_state {
+       USER_LIST_FAIL_STATE_VERSION = 0,
+       USER_LIST_FAIL_STATE_USER
+};
+
+struct _user_list_fail_server {
+       enum _user_list_fail_state state;
+};
+
+static void test_user_list_fail_input(struct server_connection *conn)
+{
+       struct _user_list_fail_server *ctx =
+               (struct _user_list_fail_server *)conn->context;
+       const char *const *args;
+       unsigned int id;
+       const char *line;
+
+       for (;;) {
+               line = i_stream_read_next_line(conn->conn.input);
+               if (line == NULL) {
+                       if (conn->conn.input->eof)
+                               server_connection_deinit(&conn);
+                       return;
+               }
+
+               switch (ctx->state) {
+               case USER_LIST_FAIL_STATE_VERSION:
+                       if (!str_begins_with(line, "VERSION\t")) {
+                               i_error("Bad VERSION");
+                               server_connection_deinit(&conn);
+                               return;
+                       }
+                       ctx->state = USER_LIST_FAIL_STATE_USER;
+                       continue;
+               case USER_LIST_FAIL_STATE_USER:
+                       args = t_strsplit_tabescaped(line);
+                       if (strcmp(args[0], "LIST") != 0 || args[1] == NULL ||
+                           str_to_uint(args[1], &id) < 0) {
+                               i_error("Bad LIST request");
+                               server_connection_deinit(&conn);
+                               return;
+                       }
+                       line = t_strdup_printf("DONE\t%u\tfail\n", id);
+                       o_stream_nsend_str(conn->conn.output, line);
+                       server_connection_deinit(&conn);
+                       return;
+               }
+               i_unreached();
+       }
+}
+
+static void test_user_list_fail_init(struct server_connection *conn)
+{
+       struct _user_list_fail_server *ctx;
+
+       ctx = p_new(conn->pool, struct _user_list_fail_server, 1);
+       conn->context = (void*)ctx;
+
+       o_stream_nsend_str(conn->conn.output, "VERSION\t1\t0\n");
+       o_stream_nsend_str(conn->conn.output, "SPID\t23234\n");
+}
+
+static void test_server_user_list_fail(void)
+{
+       test_server_init = test_user_list_fail_init;
+       test_server_input = test_user_list_fail_input;
+       test_server_run();
+}
+
+/* client */
+
+static bool test_client_user_list_fail(void)
+{
+       int ret;
+
+       ret = test_client_user_list_simple();
+       test_out("run (ret < 0)", ret < 0);
+
+       return FALSE;
+}
+
+/* test */
+
+static void test_user_list_fail(void)
+{
+       test_begin("user list fail");
+       test_expect_errors(1);
+       test_run_client_server(test_client_user_list_fail,
+                              test_server_user_list_fail);
+       test_end();
+}
+
+/*
+ * Passdb lookup
+ */
+
+/* server */
+
+enum _passdb_lookup_state {
+       PASSDB_LOOKUP_STATE_VERSION = 0,
+       PASSDB_LOOKUP_STATE_PASS
+};
+
+struct _passdb_lookup_server {
+       enum _passdb_lookup_state state;
+};
+
+static void test_passdb_lookup_input(struct server_connection *conn)
+{
+       struct _passdb_lookup_server *ctx =
+               (struct _passdb_lookup_server *)conn->context;
+       const char *const *args;
+       unsigned int id;
+       const char *line;
+
+       for (;;) {
+               line = i_stream_read_next_line(conn->conn.input);
+               if (line == NULL) {
+                       if (conn->conn.input->eof)
+                               server_connection_deinit(&conn);
+                       return;
+               }
+
+               switch (ctx->state) {
+               case PASSDB_LOOKUP_STATE_VERSION:
+                       if (!str_begins_with(line, "VERSION\t")) {
+                               i_error("Bad VERSION");
+                               server_connection_deinit(&conn);
+                               return;
+                       }
+                       ctx->state = PASSDB_LOOKUP_STATE_PASS;
+                       continue;
+               case PASSDB_LOOKUP_STATE_PASS:
+                       args = t_strsplit_tabescaped(line);
+                       if (strcmp(args[0], "PASS") != 0 || args[1] == NULL ||
+                           str_to_uint(args[1], &id) < 0) {
+                               i_error("Bad PASS request");
+                               server_connection_deinit(&conn);
+                               return;
+                       }
+                       line = t_strdup_printf("PASS\t%u\tuser=frop\n", id);
+                       o_stream_nsend_str(conn->conn.output, line);
+                       server_connection_deinit(&conn);
+                       return;
+               }
+               i_unreached();
+       }
+}
+
+static void test_passdb_lookup_init(struct server_connection *conn)
+{
+       struct _passdb_lookup_server *ctx;
+
+       ctx = p_new(conn->pool, struct _passdb_lookup_server, 1);
+       conn->context = (void*)ctx;
+
+       o_stream_nsend_str(conn->conn.output, "VERSION\t1\t0\n");
+       o_stream_nsend_str(conn->conn.output, "SPID\t23234\n");
+}
+
+static void test_server_passdb_lookup(void)
+{
+       test_server_init = test_passdb_lookup_init;
+       test_server_input = test_passdb_lookup_input;
+       test_server_run();
+}
+
+/* client */
+
+static bool test_client_passdb_lookup(void)
+{
+       const char *error;
+       int ret;
+
+       ret = test_client_passdb_lookup_simple("harrie", FALSE, &error);
+       test_out("run (ret > 0)", ret > 0);
+
+       return FALSE;
+}
+
+/* test */
+
+static void test_passdb_lookup(void)
+{
+       test_begin("passdb lookup");
+       test_run_client_server(test_client_passdb_lookup,
+                              test_server_passdb_lookup);
+       test_end();
+}
+
+/*
+ * Userdb lookup
+ */
+
+/* server */
+
+enum _userdb_lookup_state {
+       USERDB_LOOKUP_STATE_VERSION = 0,
+       USERDB_LOOKUP_STATE_PASS
+};
+
+struct _userdb_lookup_server {
+       enum _userdb_lookup_state state;
+};
+
+static void test_userdb_lookup_input(struct server_connection *conn)
 {
-       static void (*const test_functions[])(void) = {
-               test_auth_user_info_export,
-               NULL
-       };
-       return test_run(test_functions);
+       struct _userdb_lookup_server *ctx =
+               (struct _userdb_lookup_server *)conn->context;
+       const char *const *args;
+       unsigned int id;
+       const char *line;
+
+       for (;;) {
+               line = i_stream_read_next_line(conn->conn.input);
+               if (line == NULL) {
+                       if (conn->conn.input->eof)
+                               server_connection_deinit(&conn);
+                       return;
+               }
+
+               switch (ctx->state) {
+               case USERDB_LOOKUP_STATE_VERSION:
+                       if (!str_begins_with(line, "VERSION\t")) {
+                               i_error("Bad VERSION");
+                               server_connection_deinit(&conn);
+                               return;
+                       }
+                       ctx->state = USERDB_LOOKUP_STATE_PASS;
+                       continue;
+               case USERDB_LOOKUP_STATE_PASS:
+                       args = t_strsplit_tabescaped(line);
+                       if (strcmp(args[0], "USER") != 0 || args[1] == NULL ||
+                           str_to_uint(args[1], &id) < 0) {
+                               i_error("Bad PASS request");
+                               server_connection_deinit(&conn);
+                               return;
+                       }
+                       line = t_strdup_printf(
+                               "USER\t%u\tharrie\t"
+                               "uid=1000\tgid=110\thome=/home/harrie\n", id);
+                       o_stream_nsend_str(conn->conn.output, line);
+                       server_connection_deinit(&conn);
+                       return;
+               }
+               i_unreached();
+       }
+}
+
+static void test_userdb_lookup_init(struct server_connection *conn)
+{
+       struct _userdb_lookup_server *ctx;
+
+       ctx = p_new(conn->pool, struct _userdb_lookup_server, 1);
+       conn->context = (void*)ctx;
+
+       o_stream_nsend_str(conn->conn.output, "VERSION\t1\t0\n");
+       o_stream_nsend_str(conn->conn.output, "SPID\t23234\n");
+}
+
+static void test_server_userdb_lookup(void)
+{
+       test_server_init = test_userdb_lookup_init;
+       test_server_input = test_userdb_lookup_input;
+       test_server_run();
+}
+
+/* client */
+
+static bool test_client_userdb_lookup(void)
+{
+       const char *error;
+       int ret;
+
+       ret = test_client_userdb_lookup_simple("harrie", FALSE, &error);
+       test_out("run (ret > 0)", ret > 0);
+
+       return FALSE;
+}
+
+/* test */
+
+static void test_userdb_lookup(void)
+{
+       test_begin("userdb lookup");
+       test_run_client_server(test_client_userdb_lookup,
+                              test_server_userdb_lookup);
+       test_end();
+}
+
+/*
+ * User list
+ */
+
+/* server */
+
+enum _user_list_state {
+       USER_LIST_STATE_VERSION = 0,
+       USER_LIST_STATE_USER
+};
+
+struct _user_list_server {
+       enum _user_list_state state;
+};
+
+static void test_user_list_input(struct server_connection *conn)
+{
+       struct _user_list_server *ctx =
+               (struct _user_list_server *)conn->context;
+       const char *line;
+       const char *const *args;
+       unsigned int id;
+       string_t *str;
+
+       for (;;) {
+               line = i_stream_read_next_line(conn->conn.input);
+               if (line == NULL) {
+                       if (conn->conn.input->eof)
+                               server_connection_deinit(&conn);
+                       return;
+               }
+
+               switch (ctx->state) {
+               case USER_LIST_STATE_VERSION:
+                       if (!str_begins_with(line, "VERSION\t")) {
+                               i_error("Bad VERSION");
+                               server_connection_deinit(&conn);
+                               return;
+                       }
+                       ctx->state = USER_LIST_STATE_USER;
+                       continue;
+               case USER_LIST_STATE_USER:
+                       args = t_strsplit_tabescaped(line);
+                       if (strcmp(args[0], "LIST") != 0 || args[1] == NULL ||
+                           str_to_uint(args[1], &id) < 0) {
+                               i_error("Bad LIST request");
+                               server_connection_deinit(&conn);
+                               return;
+                       }
+                       str = t_str_new(256);
+                       str_printfa(str, "LIST\t%u\tuser1\n", id);
+                       str_printfa(str, "LIST\t%u\tuser2\n", id);
+                       str_printfa(str, "LIST\t%u\tuser3\n", id);
+                       str_printfa(str, "LIST\t%u\tuser4\n", id);
+                       str_printfa(str, "DONE\t%u\n", id);
+                       o_stream_nsend_str(conn->conn.output, str_c(str));
+                       server_connection_deinit(&conn);
+                       return;
+               }
+               i_unreached();
+       }
+}
+
+static void test_user_list_init(struct server_connection *conn)
+{
+       struct _user_list_server *ctx;
+
+       ctx = p_new(conn->pool, struct _user_list_server, 1);
+       conn->context = (void*)ctx;
+
+       o_stream_nsend_str(conn->conn.output, "VERSION\t1\t0\n");
+       o_stream_nsend_str(conn->conn.output, "SPID\t23234\n");
+}
+
+static void test_server_user_list(void)
+{
+       test_server_init = test_user_list_init;
+       test_server_input = test_user_list_input;
+       test_server_run();
+}
+
+/* client */
+
+static bool test_client_user_list(void)
+{
+       int ret;
+
+       ret = test_client_user_list_simple();
+       test_out("run (ret == 0)", ret == 0);
+
+       return FALSE;
+}
+
+/* test */
+
+static void test_user_list(void)
+{
+       test_begin("user list");
+       test_expect_errors(0);
+       test_run_client_server(test_client_user_list,
+                              test_server_user_list);
+       test_end();
+}
+
+/*
+ * All tests
+ */
+
+static void (*const test_functions[])(void) = {
+       test_auth_user_info_export,
+       test_connection_refused,
+       test_connection_timed_out,
+       test_bad_version,
+       test_disconnect_version,
+       test_passdb_fail,
+       test_userdb_fail,
+       test_user_list_fail,
+       test_passdb_lookup,
+       test_userdb_lookup,
+       test_user_list,
+       NULL
+};
+
+/*
+ * Test client
+ */
+
+static void test_client_deinit(void)
+{
+}
+
+static int
+test_client_passdb_lookup_simple(const char *username, bool retry,
+                                const char **error_r)
+{
+       struct auth_master_connection *auth_conn;
+       enum auth_master_flags flags = 0;
+       struct auth_user_info info;
+       const char *const *fields;
+       pool_t pool;
+       int ret;
+
+       i_zero(&info);
+       info.service = "test";
+       info.debug = debug;
+
+       if (debug)
+               flags |= AUTH_MASTER_FLAG_DEBUG;
+
+       pool = pool_alloconly_create("test", 1024);
+
+       auth_conn = auth_master_init(TEST_SOCKET, flags);
+       auth_master_set_timeout(auth_conn, 1000);
+       ret = auth_master_pass_lookup(auth_conn, username, &info,
+                                     pool, &fields);
+       if (ret < 0 && retry) {
+               ret = auth_master_pass_lookup(auth_conn, username, &info,
+                                             pool, &fields);
+       }
+       auth_master_deinit(&auth_conn);
+
+       *error_r = (ret < 0 ? t_strdup(fields[0]) : NULL);
+       pool_unref(&pool);
+
+       return ret;
+}
+
+static int
+test_client_userdb_lookup_simple(const char *username, bool retry,
+                                const char **error_r)
+{
+       struct auth_master_connection *auth_conn;
+       enum auth_master_flags flags = 0;
+       struct auth_user_info info;
+       const char *const *fields;
+       const char *username_out;
+       pool_t pool;
+       int ret;
+
+       i_zero(&info);
+       info.service = "test";
+       info.debug = debug;
+
+       if (debug)
+               flags |= AUTH_MASTER_FLAG_DEBUG;
+
+       pool = pool_alloconly_create("test", 1024);
+
+       auth_conn = auth_master_init(TEST_SOCKET, flags);
+       auth_master_set_timeout(auth_conn, 1000);
+       ret = auth_master_user_lookup(auth_conn, username, &info,
+                                     pool, &username_out, &fields);
+       if (ret < 0 && retry) {
+               ret = auth_master_user_lookup(auth_conn, username, &info,
+                                             pool, &username_out, &fields);
+       }
+       auth_master_deinit(&auth_conn);
+
+       *error_r = (ret < 0 ? t_strdup(fields[0]) : NULL);
+       pool_unref(&pool);
+
+       return ret;
+}
+
+static int test_client_user_list_simple(void)
+{
+       struct auth_master_connection *auth_conn;
+       struct auth_master_user_list_ctx *list_ctx;
+       enum auth_master_flags flags = 0;
+       struct auth_user_info info;
+       int ret;
+
+       i_zero(&info);
+       info.service = "test";
+       info.debug = debug;
+
+       if (debug)
+               flags |= AUTH_MASTER_FLAG_DEBUG;
+
+       auth_conn = auth_master_init(TEST_SOCKET, flags);
+       auth_master_set_timeout(auth_conn, 1000);
+       list_ctx = auth_master_user_list_init(auth_conn, "*", &info);
+       while (auth_master_user_list_next(list_ctx) != NULL);
+       ret = auth_master_user_list_deinit(&list_ctx);
+       auth_master_deinit(&auth_conn);
+
+       return ret;
+}
+
+/*
+ * Test server
+ */
+
+/* client connection */
+
+static void server_connection_input(struct connection *_conn)
+{
+       struct server_connection *conn = (struct server_connection *)_conn;
+
+       test_server_input(conn);
+}
+
+static void server_connection_init(int fd)
+{
+       struct server_connection *conn;
+       pool_t pool;
+
+       net_set_nonblock(fd, TRUE);
+
+       pool = pool_alloconly_create("server connection", 256);
+       conn = p_new(pool, struct server_connection, 1);
+       conn->pool = pool;
+
+       connection_init_server(server_conn_list, &conn->conn,
+                              "server connection", fd, fd);
+
+       if (test_server_init != NULL)
+               test_server_init(conn);
+}
+
+static void server_connection_deinit(struct server_connection **_conn)
+{
+       struct server_connection *conn = *_conn;
+
+       *_conn = NULL;
+
+       if (test_server_deinit != NULL)
+               test_server_deinit(conn);
+
+       connection_deinit(&conn->conn);
+       pool_unref(&conn->pool);
+}
+
+static void server_connection_destroy(struct connection *_conn)
+{
+       struct server_connection *conn =
+               (struct server_connection *)_conn;
+
+       server_connection_deinit(&conn);
+}
+
+static void server_connection_accept(void *context ATTR_UNUSED)
+{
+       int fd;
+
+       /* accept new client */
+       fd = net_accept(fd_listen, NULL, NULL);
+       if (fd == -1)
+               return;
+       if (fd == -2) {
+               i_fatal("test server: accept() failed: %m");
+       }
+
+       server_connection_init(fd);
+}
+
+/* */
+
+static struct connection_settings server_connection_set = {
+       .input_max_size = SIZE_MAX,
+       .output_max_size = SIZE_MAX,
+       .client = FALSE
+};
+
+static const struct connection_vfuncs server_connection_vfuncs = {
+       .destroy = server_connection_destroy,
+       .input = server_connection_input
+};
+
+static void test_server_run(void)
+{
+       /* open server socket */
+       io_listen = io_add(fd_listen,
+               IO_READ, server_connection_accept, NULL);
+
+       server_conn_list = connection_list_init(&server_connection_set,
+                                               &server_connection_vfuncs);
+
+       io_loop_run(ioloop);
+
+       /* close server socket */
+       io_remove(&io_listen);
+
+       connection_list_deinit(&server_conn_list);
+}
+
+/*
+ * Tests
+ */
+
+static int test_open_server_fd(void)
+{
+       int fd;
+       i_unlink_if_exists(TEST_SOCKET);
+       fd = net_listen_unix(TEST_SOCKET, 128);
+       if (debug)
+               i_debug("server listening on "TEST_SOCKET);
+       if (fd == -1)
+               i_fatal("listen("TEST_SOCKET") failed: %m");
+       return fd;
+}
+
+static int test_run_server(test_server_init_t *server_test)
+{
+       main_deinit();
+
+       i_set_failure_prefix("SERVER: ");
+
+       if (debug)
+               i_debug("PID=%s", my_pid);
+
+       ioloop = io_loop_create();
+       server_test();
+       io_loop_destroy(&ioloop);
+
+       if (debug)
+               i_debug("Terminated");
+
+       i_close_fd(&fd_listen);
+       return 0;
+}
+
+static void test_run_client(test_client_init_t *client_test)
+{
+       i_set_failure_prefix("CLIENT: ");
+
+       if (debug)
+               i_debug("PID=%s", my_pid);
+
+       i_sleep_intr_msecs(100); /* wait a little for server setup */
+
+       ioloop = io_loop_create();
+       if (client_test())
+               io_loop_run(ioloop);
+       test_client_deinit();
+       io_loop_destroy(&ioloop);
+
+       if (debug)
+               i_debug("Terminated");
+}
+
+static void
+test_run_client_server(test_client_init_t *client_test,
+                      test_server_init_t *server_test)
+{
+       if (server_test != NULL) {
+               /* Fork server */
+               fd_listen = test_open_server_fd();
+               test_subprocess_fork(test_run_server, server_test, FALSE);
+               i_close_fd(&fd_listen);
+       }
+
+       /* Run client */
+       test_run_client(client_test);
+
+       i_unset_failure_prefix();
+       test_subprocess_kill_all(SERVER_KILL_TIMEOUT_SECS);
+}
+
+/*
+ * Main
+ */
+
+static void main_cleanup(void)
+{
+       i_unlink_if_exists(TEST_SOCKET);
+}
+
+static void main_init(void)
+{
+       /* nothing yet */
+}
+
+static void main_deinit(void)
+{
+       /* nothing yet; also called from sub-processes */
+}
+
+int main(int argc, char *argv[])
+{
+       int c;
+       int ret;
+
+       lib_init();
+       main_init();
+
+       while ((c = getopt(argc, argv, "D")) > 0) {
+               switch (c) {
+               case 'D':
+                       debug = TRUE;
+                       break;
+               default:
+                       i_fatal("Usage: %s [-D]", argv[0]);
+               }
+       }
+
+       test_subprocesses_init(debug);
+       test_subprocess_set_cleanup_callback(main_cleanup);
+
+       ret = test_run(test_functions);
+
+       test_subprocesses_deinit();
+       main_deinit();
+       lib_deinit();
+       return ret;
 }
diff --git a/src/lib-auth/test-auth-master2.c b/src/lib-auth/test-auth-master2.c
deleted file mode 100644 (file)
index 09216fa..0000000
+++ /dev/null
@@ -1,1383 +0,0 @@
-/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "str.h"
-#include "strescape.h"
-#include "hostpid.h"
-#include "ioloop.h"
-#include "istream.h"
-#include "istream-chain.h"
-#include "ostream.h"
-#include "time-util.h"
-#include "sleep.h"
-#include "unlink-directory.h"
-#include "write-full.h"
-#include "connection.h"
-#include "master-interface.h"
-#include "test-common.h"
-#include "test-subprocess.h"
-
-#include "auth-master.h"
-
-#include <unistd.h>
-
-#define TEST_SOCKET "./auth-master-test"
-#define SERVER_KILL_TIMEOUT_SECS    20
-
-static void main_deinit(void);
-
-/*
- * Types
- */
-
-struct server_connection {
-       struct connection conn;
-
-       void *context;
-
-       pool_t pool;
-};
-
-typedef void test_server_init_t(void);
-typedef bool test_client_init_t(void);
-
-/*
- * State
- */
-
-/* common */
-static struct ioloop *ioloop;
-static bool debug = FALSE;
-
-/* server */
-static struct io *io_listen;
-static int fd_listen = -1;
-static struct connection_list *server_conn_list;
-static void (*test_server_input)(struct server_connection *conn);
-static void (*test_server_init)(struct server_connection *conn);
-static void (*test_server_deinit)(struct server_connection *conn);
-
-/* client */
-
-/*
- * Forward declarations
- */
-
-/* server */
-static void test_server_run(void);
-static void server_connection_deinit(struct server_connection **_conn);
-
-/* client */
-static void test_client_deinit(void);
-
-static int
-test_client_passdb_lookup_simple(const char *user, bool retry,
-                                const char **error_r);
-static int
-test_client_userdb_lookup_simple(const char *user, bool retry,
-                                const char **error_r);
-static int test_client_user_list_simple(void);
-
-/* test*/
-static void
-test_run_client_server(test_client_init_t *client_test,
-                      test_server_init_t *server_test) ATTR_NULL(2);
-
-/*
- * Connection refused
- */
-
-/* server */
-
-static void test_server_connection_refused(void)
-{
-       i_close_fd(&fd_listen);
-       i_sleep_intr_secs(500);
-}
-
-/* client */
-
-static bool test_client_connection_refused(void)
-{
-       const char *error;
-       int ret;
-
-       ret = test_client_passdb_lookup_simple("harrie", FALSE, &error);
-       test_out_reason("run (ret == -1)", ret == -1, error);
-
-       return FALSE;
-}
-
-/* test */
-
-static void test_connection_refused(void)
-{
-       test_begin("connection refused");
-       test_expect_error_string("Connection refused");
-       test_run_client_server(test_client_connection_refused,
-                              test_server_connection_refused);
-       test_end();
-}
-
-/*
- * Connection timed out
- */
-
-/* server */
-
-static void test_connection_timed_out_input(struct server_connection *conn)
-{
-       i_sleep_intr_secs(5);
-       server_connection_deinit(&conn);
-}
-
-static void test_server_connection_timed_out(void)
-{
-       test_server_input = test_connection_timed_out_input;
-       test_server_run();
-}
-
-/* client */
-
-static bool test_client_connection_timed_out(void)
-{
-       time_t time;
-       const char *error;
-       int ret;
-
-       io_loop_time_refresh();
-       time = ioloop_time;
-
-       ret = test_client_passdb_lookup_simple("harrie", FALSE, &error);
-       test_out_reason("run (ret == -1)", ret == -1, error);
-
-       io_loop_time_refresh();
-       test_out("timeout", (ioloop_time - time) < 5);
-       return FALSE;
-}
-
-/* test */
-
-static void test_connection_timed_out(void)
-{
-       test_begin("connection timed out");
-       test_expect_error_string("Connecting timed out");
-       test_run_client_server(test_client_connection_timed_out,
-                              test_server_connection_timed_out);
-       test_end();
-}
-
-/*
- * Bad VERSION
- */
-
-/* server */
-
-static void test_bad_version_input(struct server_connection *conn)
-{
-       server_connection_deinit(&conn);
-}
-
-static void test_bad_version_init(struct server_connection *conn)
-{
-       o_stream_nsend_str(conn->conn.output, "VERSION\t666\t666\n");
-       o_stream_nsend_str(conn->conn.output, "SPID\t23234\n");
-}
-
-static void test_server_bad_version(void)
-{
-       test_server_init = test_bad_version_init;
-       test_server_input = test_bad_version_input;
-       test_server_run();
-}
-
-/* client */
-
-static bool test_client_bad_version(void)
-{
-       const char *error;
-       int ret;
-
-       ret = test_client_passdb_lookup_simple("harrie", FALSE, &error);
-       test_out_reason("run (ret == -1)", ret == -1, error);
-       return FALSE;
-}
-
-/* test */
-
-static void test_bad_version(void)
-{
-       test_begin("bad version");
-       test_expect_error_string("Socket supports major version 666");
-       test_run_client_server(test_client_bad_version,
-                              test_server_bad_version);
-       test_end();
-}
-
-/*
- * Disconnect VERSION
- */
-
-/* server */
-
-static void test_disconnect_version_input(struct server_connection *conn)
-{
-       const char *line;
-
-       line = i_stream_read_next_line(conn->conn.input);
-       if (line == NULL) {
-               if (conn->conn.input->eof)
-                       server_connection_deinit(&conn);
-               return;
-       }
-       server_connection_deinit(&conn);
-}
-
-static void test_disconnect_version_init(struct server_connection *conn)
-{
-       o_stream_nsend_str(conn->conn.output, "VERSION\t1\t0\n");
-       o_stream_nsend_str(conn->conn.output, "SPID\t23234\n");
-}
-
-static void test_server_disconnect_version(void)
-{
-       test_server_init = test_disconnect_version_init;
-       test_server_input = test_disconnect_version_input;
-       test_server_run();
-}
-
-/* client */
-
-static bool test_client_disconnect_version(void)
-{
-       const char *error;
-       int ret;
-
-       ret = test_client_passdb_lookup_simple("harrie", FALSE, &error);
-       test_out_reason("run (ret == -1)", ret == -1, error);
-       return FALSE;
-}
-
-/* test */
-
-static void test_disconnect_version(void)
-{
-       test_begin("disconnect version");
-       test_expect_error_string("Disconnected unexpectedly");
-       test_run_client_server(test_client_disconnect_version,
-                              test_server_disconnect_version);
-       test_end();
-}
-
-/*
- * Passdb FAIL
- */
-
-/* server */
-
-enum _passdb_fail_state {
-       PASSDB_FAIL_STATE_VERSION = 0,
-       PASSDB_FAIL_STATE_PASS
-};
-
-struct _passdb_fail_server {
-       enum _passdb_fail_state state;
-
-       bool not_found:1;
-};
-
-static void test_passdb_fail_input(struct server_connection *conn)
-{
-       struct _passdb_fail_server *ctx =
-               (struct _passdb_fail_server *)conn->context;
-       const char *const *args;
-       unsigned int id;
-       const char *line;
-
-       for (;;) {
-               line = i_stream_read_next_line(conn->conn.input);
-               if (line == NULL) {
-                       if (conn->conn.input->eof)
-                               server_connection_deinit(&conn);
-                       return;
-               }
-
-               switch (ctx->state) {
-               case PASSDB_FAIL_STATE_VERSION:
-                       if (!str_begins_with(line, "VERSION\t")) {
-                               i_error("Bad VERSION");
-                               server_connection_deinit(&conn);
-                               return;
-                       }
-                       ctx->state = PASSDB_FAIL_STATE_PASS;
-                       continue;
-               case PASSDB_FAIL_STATE_PASS:
-                       args = t_strsplit_tabescaped(line);
-                       if (strcmp(args[0], "PASS") != 0 || args[1] == NULL ||
-                           str_to_uint(args[1], &id) < 0 || args[2] == NULL) {
-                               i_error("Bad PASS request");
-                               server_connection_deinit(&conn);
-                               return;
-                       }
-                       if (strcmp(args[2], "henk") == 0) {
-                               line = t_strdup_printf("NOTFOUND\t%u\n", id);
-                       } else if (strcmp(args[2], "holger") == 0) {
-                               i_sleep_intr_secs(5);
-                               server_connection_deinit(&conn);
-                               return;
-                       } else if (strcmp(args[2], "hendrik") == 0) {
-                               server_connection_deinit(&conn);
-                               return;
-                       } else {
-                               line = t_strdup_printf(
-                                       "FAIL\t%u\t"
-                                       "reason=You shall not pass!!\n", id);
-                       }
-                       o_stream_nsend_str(conn->conn.output, line);
-                       server_connection_deinit(&conn);
-                       return;
-               }
-               i_unreached();
-       }
-}
-
-static void test_passdb_fail_init(struct server_connection *conn)
-{
-       struct _passdb_fail_server *ctx;
-
-       ctx = p_new(conn->pool, struct _passdb_fail_server, 1);
-       conn->context = (void*)ctx;
-
-       o_stream_nsend_str(conn->conn.output, "VERSION\t1\t0\n");
-       o_stream_nsend_str(conn->conn.output, "SPID\t23234\n");
-}
-
-static void test_server_passdb_fail(void)
-{
-       test_server_init = test_passdb_fail_init;
-       test_server_input = test_passdb_fail_input;
-       test_server_run();
-}
-
-/* client */
-
-static bool test_client_passdb_fail(void)
-{
-       const char *error;
-       int ret;
-
-       ret = test_client_passdb_lookup_simple("harrie", FALSE, &error);
-       test_out("run (ret == -2)", ret == -2);
-       test_assert(error != NULL &&
-                   strcmp(error, "You shall not pass!!") == 0);
-
-       return FALSE;
-}
-
-static bool test_client_passdb_notfound(void)
-{
-       const char *error;
-       int ret;
-
-       ret = test_client_passdb_lookup_simple("henk", FALSE, &error);
-       test_out("run (ret == 0)", ret == 0);
-       test_assert(error == NULL);
-
-       return FALSE;
-}
-
-static bool test_client_passdb_timeout(void)
-{
-       const char *error;
-       int ret;
-
-       ret = test_client_passdb_lookup_simple("holger", FALSE, &error);
-       test_out("run (ret == -1)", ret == -1);
-       test_assert(error == NULL);
-
-       return FALSE;
-}
-
-static bool test_client_passdb_disconnect(void)
-{
-       const char *error;
-       int ret;
-
-       ret = test_client_passdb_lookup_simple("hendrik", FALSE, &error);
-       test_out("run (ret == -1)", ret == -1);
-       test_assert(error == NULL);
-
-       return FALSE;
-}
-
-static bool test_client_passdb_reconnect(void)
-{
-       const char *error;
-       int ret;
-
-       ret = test_client_passdb_lookup_simple("hendrik", TRUE, &error);
-       test_out("run (ret == -1)", ret == -1);
-       test_assert(error == NULL);
-
-       return FALSE;
-}
-
-/* test */
-
-static void test_passdb_fail(void)
-{
-       test_begin("passdb fail");
-       test_run_client_server(test_client_passdb_fail,
-                              test_server_passdb_fail);
-       test_end();
-
-       test_begin("passdb notfound");
-       test_run_client_server(test_client_passdb_notfound,
-                              test_server_passdb_fail);
-       test_end();
-
-       test_begin("passdb timeout");
-       test_expect_error_string("Request timed out");
-       test_run_client_server(test_client_passdb_timeout,
-                              test_server_passdb_fail);
-       test_end();
-
-       test_begin("passdb disconnect");
-       test_expect_error_string("Disconnected unexpectedly");
-       test_run_client_server(test_client_passdb_disconnect,
-                              test_server_passdb_fail);
-       test_end();
-
-       test_begin("passdb reconnect");
-       test_expect_errors(2);
-       test_run_client_server(test_client_passdb_reconnect,
-                              test_server_passdb_fail);
-       test_end();
-}
-
-/*
- * Userdb FAIL
- */
-
-/* server */
-
-enum _userdb_fail_state {
-       USERDB_FAIL_STATE_VERSION = 0,
-       USERDB_FAIL_STATE_USER
-};
-
-struct _userdb_fail_server {
-       enum _userdb_fail_state state;
-
-       bool not_found:1;
-};
-
-static void test_userdb_fail_input(struct server_connection *conn)
-{
-       struct _userdb_fail_server *ctx =
-               (struct _userdb_fail_server *)conn->context;
-       const char *const *args;
-       unsigned int id;
-       const char *line;
-
-       for (;;) {
-               line = i_stream_read_next_line(conn->conn.input);
-               if (line == NULL) {
-                       if (conn->conn.input->eof)
-                               server_connection_deinit(&conn);
-                       return;
-               }
-
-               switch (ctx->state) {
-               case USERDB_FAIL_STATE_VERSION:
-                       if (!str_begins_with(line, "VERSION\t")) {
-                               i_error("Bad VERSION");
-                               server_connection_deinit(&conn);
-                               return;
-                       }
-                       ctx->state = USERDB_FAIL_STATE_USER;
-                       continue;
-               case USERDB_FAIL_STATE_USER:
-                       args = t_strsplit_tabescaped(line);
-                       if (strcmp(args[0], "USER") != 0 || args[1] == NULL ||
-                           str_to_uint(args[1], &id) < 0) {
-                               i_error("Bad USER request");
-                               server_connection_deinit(&conn);
-                               return;
-                       }
-                       if (strcmp(args[2], "henk") == 0) {
-                               line = t_strdup_printf("NOTFOUND\t%u\n", id);
-                       } else if (strcmp(args[2], "holger") == 0) {
-                               i_sleep_intr_secs(5);
-                               server_connection_deinit(&conn);
-                               return;
-                       } else if (strcmp(args[2], "hendrik") == 0) {
-                               server_connection_deinit(&conn);
-                               return;
-                       } else {
-                               line = t_strdup_printf("FAIL\t%u\t"
-                                       "reason=It is no use!\n", id);
-                       }
-                       o_stream_nsend_str(conn->conn.output, line);
-                       server_connection_deinit(&conn);
-                       return;
-               }
-               i_unreached();
-       }
-}
-
-static void test_userdb_fail_init(struct server_connection *conn)
-{
-       struct _userdb_fail_server *ctx;
-
-       ctx = p_new(conn->pool, struct _userdb_fail_server, 1);
-       conn->context = (void*)ctx;
-
-       o_stream_nsend_str(conn->conn.output, "VERSION\t1\t0\n");
-       o_stream_nsend_str(conn->conn.output, "SPID\t23234\n");
-}
-
-static void test_server_userdb_fail(void)
-{
-       test_server_init = test_userdb_fail_init;
-       test_server_input = test_userdb_fail_input;
-       test_server_run();
-}
-
-/* client */
-
-static bool test_client_userdb_fail(void)
-{
-       const char *error;
-       int ret;
-
-       ret = test_client_userdb_lookup_simple("harrie", FALSE, &error);
-       test_out("run (ret == -2)", ret == -2);
-       test_assert(error != NULL &&
-                   strcmp(error, "It is no use!") == 0);
-
-       return FALSE;
-}
-
-static bool test_client_userdb_notfound(void)
-{
-       const char *error;
-       int ret;
-
-       ret = test_client_userdb_lookup_simple("henk", FALSE, &error);
-       test_out("run (ret == 0)", ret == 0);
-       test_assert(error == NULL);
-
-       return FALSE;
-}
-
-static bool test_client_userdb_timeout(void)
-{
-       const char *error;
-       int ret;
-
-       ret = test_client_userdb_lookup_simple("holger", FALSE, &error);
-       test_out("run (ret == -1)", ret == -1);
-       test_assert(error == NULL);
-
-       return FALSE;
-}
-
-static bool test_client_userdb_disconnect(void)
-{
-       const char *error;
-       int ret;
-
-       ret = test_client_userdb_lookup_simple("hendrik", FALSE, &error);
-       test_out("run (ret == -1)", ret == -1);
-       test_assert(error == NULL);
-
-       return FALSE;
-}
-
-static bool test_client_userdb_reconnect(void)
-{
-       const char *error;
-       int ret;
-
-       ret = test_client_userdb_lookup_simple("hendrik", TRUE, &error);
-       test_out("run (ret == -1)", ret == -1);
-       test_assert(error == NULL);
-
-       return FALSE;
-}
-
-/* test */
-
-static void test_userdb_fail(void)
-{
-       test_begin("userdb fail");
-       test_run_client_server(test_client_userdb_fail,
-                              test_server_userdb_fail);
-       test_end();
-
-       test_begin("userdb notfound");
-       test_run_client_server(test_client_userdb_notfound,
-                              test_server_userdb_fail);
-       test_end();
-
-       test_begin("userdb timeout");
-       test_expect_error_string("Request timed out");
-       test_run_client_server(test_client_userdb_timeout,
-                              test_server_userdb_fail);
-       test_end();
-
-       test_begin("userdb disconnect");
-       test_expect_error_string("Disconnected unexpectedly");
-       test_run_client_server(test_client_userdb_disconnect,
-                              test_server_userdb_fail);
-       test_end();
-
-       test_begin("userdb reconnect");
-       test_expect_errors(2);
-       test_run_client_server(test_client_userdb_reconnect,
-                              test_server_userdb_fail);
-       test_end();
-}
-
-/*
- * User list FAIL
- */
-
-/* server */
-
-enum _user_list_fail_state {
-       USER_LIST_FAIL_STATE_VERSION = 0,
-       USER_LIST_FAIL_STATE_USER
-};
-
-struct _user_list_fail_server {
-       enum _user_list_fail_state state;
-};
-
-static void test_user_list_fail_input(struct server_connection *conn)
-{
-       struct _user_list_fail_server *ctx =
-               (struct _user_list_fail_server *)conn->context;
-       const char *const *args;
-       unsigned int id;
-       const char *line;
-
-       for (;;) {
-               line = i_stream_read_next_line(conn->conn.input);
-               if (line == NULL) {
-                       if (conn->conn.input->eof)
-                               server_connection_deinit(&conn);
-                       return;
-               }
-
-               switch (ctx->state) {
-               case USER_LIST_FAIL_STATE_VERSION:
-                       if (!str_begins_with(line, "VERSION\t")) {
-                               i_error("Bad VERSION");
-                               server_connection_deinit(&conn);
-                               return;
-                       }
-                       ctx->state = USER_LIST_FAIL_STATE_USER;
-                       continue;
-               case USER_LIST_FAIL_STATE_USER:
-                       args = t_strsplit_tabescaped(line);
-                       if (strcmp(args[0], "LIST") != 0 || args[1] == NULL ||
-                           str_to_uint(args[1], &id) < 0) {
-                               i_error("Bad LIST request");
-                               server_connection_deinit(&conn);
-                               return;
-                       }
-                       line = t_strdup_printf("DONE\t%u\tfail\n", id);
-                       o_stream_nsend_str(conn->conn.output, line);
-                       server_connection_deinit(&conn);
-                       return;
-               }
-               i_unreached();
-       }
-}
-
-static void test_user_list_fail_init(struct server_connection *conn)
-{
-       struct _user_list_fail_server *ctx;
-
-       ctx = p_new(conn->pool, struct _user_list_fail_server, 1);
-       conn->context = (void*)ctx;
-
-       o_stream_nsend_str(conn->conn.output, "VERSION\t1\t0\n");
-       o_stream_nsend_str(conn->conn.output, "SPID\t23234\n");
-}
-
-static void test_server_user_list_fail(void)
-{
-       test_server_init = test_user_list_fail_init;
-       test_server_input = test_user_list_fail_input;
-       test_server_run();
-}
-
-/* client */
-
-static bool test_client_user_list_fail(void)
-{
-       int ret;
-
-       ret = test_client_user_list_simple();
-       test_out("run (ret < 0)", ret < 0);
-
-       return FALSE;
-}
-
-/* test */
-
-static void test_user_list_fail(void)
-{
-       test_begin("user list fail");
-       test_expect_errors(1);
-       test_run_client_server(test_client_user_list_fail,
-                              test_server_user_list_fail);
-       test_end();
-}
-
-/*
- * Passdb lookup
- */
-
-/* server */
-
-enum _passdb_lookup_state {
-       PASSDB_LOOKUP_STATE_VERSION = 0,
-       PASSDB_LOOKUP_STATE_PASS
-};
-
-struct _passdb_lookup_server {
-       enum _passdb_lookup_state state;
-};
-
-static void test_passdb_lookup_input(struct server_connection *conn)
-{
-       struct _passdb_lookup_server *ctx =
-               (struct _passdb_lookup_server *)conn->context;
-       const char *const *args;
-       unsigned int id;
-       const char *line;
-
-       for (;;) {
-               line = i_stream_read_next_line(conn->conn.input);
-               if (line == NULL) {
-                       if (conn->conn.input->eof)
-                               server_connection_deinit(&conn);
-                       return;
-               }
-
-               switch (ctx->state) {
-               case PASSDB_LOOKUP_STATE_VERSION:
-                       if (!str_begins_with(line, "VERSION\t")) {
-                               i_error("Bad VERSION");
-                               server_connection_deinit(&conn);
-                               return;
-                       }
-                       ctx->state = PASSDB_LOOKUP_STATE_PASS;
-                       continue;
-               case PASSDB_LOOKUP_STATE_PASS:
-                       args = t_strsplit_tabescaped(line);
-                       if (strcmp(args[0], "PASS") != 0 || args[1] == NULL ||
-                           str_to_uint(args[1], &id) < 0) {
-                               i_error("Bad PASS request");
-                               server_connection_deinit(&conn);
-                               return;
-                       }
-                       line = t_strdup_printf("PASS\t%u\tuser=frop\n", id);
-                       o_stream_nsend_str(conn->conn.output, line);
-                       server_connection_deinit(&conn);
-                       return;
-               }
-               i_unreached();
-       }
-}
-
-static void test_passdb_lookup_init(struct server_connection *conn)
-{
-       struct _passdb_lookup_server *ctx;
-
-       ctx = p_new(conn->pool, struct _passdb_lookup_server, 1);
-       conn->context = (void*)ctx;
-
-       o_stream_nsend_str(conn->conn.output, "VERSION\t1\t0\n");
-       o_stream_nsend_str(conn->conn.output, "SPID\t23234\n");
-}
-
-static void test_server_passdb_lookup(void)
-{
-       test_server_init = test_passdb_lookup_init;
-       test_server_input = test_passdb_lookup_input;
-       test_server_run();
-}
-
-/* client */
-
-static bool test_client_passdb_lookup(void)
-{
-       const char *error;
-       int ret;
-
-       ret = test_client_passdb_lookup_simple("harrie", FALSE, &error);
-       test_out("run (ret > 0)", ret > 0);
-
-       return FALSE;
-}
-
-/* test */
-
-static void test_passdb_lookup(void)
-{
-       test_begin("passdb lookup");
-       test_run_client_server(test_client_passdb_lookup,
-                              test_server_passdb_lookup);
-       test_end();
-}
-
-/*
- * Userdb lookup
- */
-
-/* server */
-
-enum _userdb_lookup_state {
-       USERDB_LOOKUP_STATE_VERSION = 0,
-       USERDB_LOOKUP_STATE_PASS
-};
-
-struct _userdb_lookup_server {
-       enum _userdb_lookup_state state;
-};
-
-static void test_userdb_lookup_input(struct server_connection *conn)
-{
-       struct _userdb_lookup_server *ctx =
-               (struct _userdb_lookup_server *)conn->context;
-       const char *const *args;
-       unsigned int id;
-       const char *line;
-
-       for (;;) {
-               line = i_stream_read_next_line(conn->conn.input);
-               if (line == NULL) {
-                       if (conn->conn.input->eof)
-                               server_connection_deinit(&conn);
-                       return;
-               }
-
-               switch (ctx->state) {
-               case USERDB_LOOKUP_STATE_VERSION:
-                       if (!str_begins_with(line, "VERSION\t")) {
-                               i_error("Bad VERSION");
-                               server_connection_deinit(&conn);
-                               return;
-                       }
-                       ctx->state = USERDB_LOOKUP_STATE_PASS;
-                       continue;
-               case USERDB_LOOKUP_STATE_PASS:
-                       args = t_strsplit_tabescaped(line);
-                       if (strcmp(args[0], "USER") != 0 || args[1] == NULL ||
-                           str_to_uint(args[1], &id) < 0) {
-                               i_error("Bad PASS request");
-                               server_connection_deinit(&conn);
-                               return;
-                       }
-                       line = t_strdup_printf(
-                               "USER\t%u\tharrie\t"
-                               "uid=1000\tgid=110\thome=/home/harrie\n", id);
-                       o_stream_nsend_str(conn->conn.output, line);
-                       server_connection_deinit(&conn);
-                       return;
-               }
-               i_unreached();
-       }
-}
-
-static void test_userdb_lookup_init(struct server_connection *conn)
-{
-       struct _userdb_lookup_server *ctx;
-
-       ctx = p_new(conn->pool, struct _userdb_lookup_server, 1);
-       conn->context = (void*)ctx;
-
-       o_stream_nsend_str(conn->conn.output, "VERSION\t1\t0\n");
-       o_stream_nsend_str(conn->conn.output, "SPID\t23234\n");
-}
-
-static void test_server_userdb_lookup(void)
-{
-       test_server_init = test_userdb_lookup_init;
-       test_server_input = test_userdb_lookup_input;
-       test_server_run();
-}
-
-/* client */
-
-static bool test_client_userdb_lookup(void)
-{
-       const char *error;
-       int ret;
-
-       ret = test_client_userdb_lookup_simple("harrie", FALSE, &error);
-       test_out("run (ret > 0)", ret > 0);
-
-       return FALSE;
-}
-
-/* test */
-
-static void test_userdb_lookup(void)
-{
-       test_begin("userdb lookup");
-       test_run_client_server(test_client_userdb_lookup,
-                              test_server_userdb_lookup);
-       test_end();
-}
-
-/*
- * User list
- */
-
-/* server */
-
-enum _user_list_state {
-       USER_LIST_STATE_VERSION = 0,
-       USER_LIST_STATE_USER
-};
-
-struct _user_list_server {
-       enum _user_list_state state;
-};
-
-static void test_user_list_input(struct server_connection *conn)
-{
-       struct _user_list_server *ctx =
-               (struct _user_list_server *)conn->context;
-       const char *line;
-       const char *const *args;
-       unsigned int id;
-       string_t *str;
-
-       for (;;) {
-               line = i_stream_read_next_line(conn->conn.input);
-               if (line == NULL) {
-                       if (conn->conn.input->eof)
-                               server_connection_deinit(&conn);
-                       return;
-               }
-
-               switch (ctx->state) {
-               case USER_LIST_STATE_VERSION:
-                       if (!str_begins_with(line, "VERSION\t")) {
-                               i_error("Bad VERSION");
-                               server_connection_deinit(&conn);
-                               return;
-                       }
-                       ctx->state = USER_LIST_STATE_USER;
-                       continue;
-               case USER_LIST_STATE_USER:
-                       args = t_strsplit_tabescaped(line);
-                       if (strcmp(args[0], "LIST") != 0 || args[1] == NULL ||
-                           str_to_uint(args[1], &id) < 0) {
-                               i_error("Bad LIST request");
-                               server_connection_deinit(&conn);
-                               return;
-                       }
-                       str = t_str_new(256);
-                       str_printfa(str, "LIST\t%u\tuser1\n", id);
-                       str_printfa(str, "LIST\t%u\tuser2\n", id);
-                       str_printfa(str, "LIST\t%u\tuser3\n", id);
-                       str_printfa(str, "LIST\t%u\tuser4\n", id);
-                       str_printfa(str, "DONE\t%u\n", id);
-                       o_stream_nsend_str(conn->conn.output, str_c(str));
-                       server_connection_deinit(&conn);
-                       return;
-               }
-               i_unreached();
-       }
-}
-
-static void test_user_list_init(struct server_connection *conn)
-{
-       struct _user_list_server *ctx;
-
-       ctx = p_new(conn->pool, struct _user_list_server, 1);
-       conn->context = (void*)ctx;
-
-       o_stream_nsend_str(conn->conn.output, "VERSION\t1\t0\n");
-       o_stream_nsend_str(conn->conn.output, "SPID\t23234\n");
-}
-
-static void test_server_user_list(void)
-{
-       test_server_init = test_user_list_init;
-       test_server_input = test_user_list_input;
-       test_server_run();
-}
-
-/* client */
-
-static bool test_client_user_list(void)
-{
-       int ret;
-
-       ret = test_client_user_list_simple();
-       test_out("run (ret == 0)", ret == 0);
-
-       return FALSE;
-}
-
-/* test */
-
-static void test_user_list(void)
-{
-       test_begin("user list");
-       test_expect_errors(0);
-       test_run_client_server(test_client_user_list,
-                              test_server_user_list);
-       test_end();
-}
-
-/*
- * All tests
- */
-
-static void (*const test_functions[])(void) = {
-       test_connection_refused,
-       test_connection_timed_out,
-       test_bad_version,
-       test_disconnect_version,
-       test_passdb_fail,
-       test_userdb_fail,
-       test_user_list_fail,
-       test_passdb_lookup,
-       test_userdb_lookup,
-       test_user_list,
-       NULL
-};
-
-/*
- * Test client
- */
-
-static void test_client_deinit(void)
-{
-}
-
-static int
-test_client_passdb_lookup_simple(const char *username, bool retry,
-                                const char **error_r)
-{
-       struct auth_master_connection *auth_conn;
-       enum auth_master_flags flags = 0;
-       struct auth_user_info info;
-       const char *const *fields;
-       pool_t pool;
-       int ret;
-
-       i_zero(&info);
-       info.service = "test";
-       info.debug = debug;
-
-       if (debug)
-               flags |= AUTH_MASTER_FLAG_DEBUG;
-
-       pool = pool_alloconly_create("test", 1024);
-
-       auth_conn = auth_master_init(TEST_SOCKET, flags);
-       auth_master_set_timeout(auth_conn, 1000);
-       ret = auth_master_pass_lookup(auth_conn, username, &info,
-                                     pool, &fields);
-       if (ret < 0 && retry) {
-               ret = auth_master_pass_lookup(auth_conn, username, &info,
-                                             pool, &fields);
-       }
-       auth_master_deinit(&auth_conn);
-
-       *error_r = (ret < 0 ? t_strdup(fields[0]) : NULL);
-       pool_unref(&pool);
-
-       return ret;
-}
-
-static int
-test_client_userdb_lookup_simple(const char *username, bool retry,
-                                const char **error_r)
-{
-       struct auth_master_connection *auth_conn;
-       enum auth_master_flags flags = 0;
-       struct auth_user_info info;
-       const char *const *fields;
-       const char *username_out;
-       pool_t pool;
-       int ret;
-
-       i_zero(&info);
-       info.service = "test";
-       info.debug = debug;
-
-       if (debug)
-               flags |= AUTH_MASTER_FLAG_DEBUG;
-
-       pool = pool_alloconly_create("test", 1024);
-
-       auth_conn = auth_master_init(TEST_SOCKET, flags);
-       auth_master_set_timeout(auth_conn, 1000);
-       ret = auth_master_user_lookup(auth_conn, username, &info,
-                                     pool, &username_out, &fields);
-       if (ret < 0 && retry) {
-               ret = auth_master_user_lookup(auth_conn, username, &info,
-                                             pool, &username_out, &fields);
-       }
-       auth_master_deinit(&auth_conn);
-
-       *error_r = (ret < 0 ? t_strdup(fields[0]) : NULL);
-       pool_unref(&pool);
-
-       return ret;
-}
-
-static int test_client_user_list_simple(void)
-{
-       struct auth_master_connection *auth_conn;
-       struct auth_master_user_list_ctx *list_ctx;
-       enum auth_master_flags flags = 0;
-       struct auth_user_info info;
-       int ret;
-
-       i_zero(&info);
-       info.service = "test";
-       info.debug = debug;
-
-       if (debug)
-               flags |= AUTH_MASTER_FLAG_DEBUG;
-
-       auth_conn = auth_master_init(TEST_SOCKET, flags);
-       auth_master_set_timeout(auth_conn, 1000);
-       list_ctx = auth_master_user_list_init(auth_conn, "*", &info);
-       while (auth_master_user_list_next(list_ctx) != NULL);
-       ret = auth_master_user_list_deinit(&list_ctx);
-       auth_master_deinit(&auth_conn);
-
-       return ret;
-}
-
-/*
- * Test server
- */
-
-/* client connection */
-
-static void server_connection_input(struct connection *_conn)
-{
-       struct server_connection *conn = (struct server_connection *)_conn;
-
-       test_server_input(conn);
-}
-
-static void server_connection_init(int fd)
-{
-       struct server_connection *conn;
-       pool_t pool;
-
-       net_set_nonblock(fd, TRUE);
-
-       pool = pool_alloconly_create("server connection", 256);
-       conn = p_new(pool, struct server_connection, 1);
-       conn->pool = pool;
-
-       connection_init_server(server_conn_list, &conn->conn,
-                              "server connection", fd, fd);
-
-       if (test_server_init != NULL)
-               test_server_init(conn);
-}
-
-static void server_connection_deinit(struct server_connection **_conn)
-{
-       struct server_connection *conn = *_conn;
-
-       *_conn = NULL;
-
-       if (test_server_deinit != NULL)
-               test_server_deinit(conn);
-
-       connection_deinit(&conn->conn);
-       pool_unref(&conn->pool);
-}
-
-static void server_connection_destroy(struct connection *_conn)
-{
-       struct server_connection *conn =
-               (struct server_connection *)_conn;
-
-       server_connection_deinit(&conn);
-}
-
-static void server_connection_accept(void *context ATTR_UNUSED)
-{
-       int fd;
-
-       /* accept new client */
-       fd = net_accept(fd_listen, NULL, NULL);
-       if (fd == -1)
-               return;
-       if (fd == -2) {
-               i_fatal("test server: accept() failed: %m");
-       }
-
-       server_connection_init(fd);
-}
-
-/* */
-
-static struct connection_settings server_connection_set = {
-       .input_max_size = SIZE_MAX,
-       .output_max_size = SIZE_MAX,
-       .client = FALSE
-};
-
-static const struct connection_vfuncs server_connection_vfuncs = {
-       .destroy = server_connection_destroy,
-       .input = server_connection_input
-};
-
-static void test_server_run(void)
-{
-       /* open server socket */
-       io_listen = io_add(fd_listen,
-               IO_READ, server_connection_accept, NULL);
-
-       server_conn_list = connection_list_init(&server_connection_set,
-                                               &server_connection_vfuncs);
-
-       io_loop_run(ioloop);
-
-       /* close server socket */
-       io_remove(&io_listen);
-
-       connection_list_deinit(&server_conn_list);
-}
-
-/*
- * Tests
- */
-
-static int test_open_server_fd(void)
-{
-       int fd;
-       i_unlink_if_exists(TEST_SOCKET);
-       fd = net_listen_unix(TEST_SOCKET, 128);
-       if (debug)
-               i_debug("server listening on "TEST_SOCKET);
-       if (fd == -1)
-               i_fatal("listen("TEST_SOCKET") failed: %m");
-       return fd;
-}
-
-static int test_run_server(test_server_init_t *server_test)
-{
-       main_deinit();
-
-       i_set_failure_prefix("SERVER: ");
-
-       if (debug)
-               i_debug("PID=%s", my_pid);
-
-       ioloop = io_loop_create();
-       server_test();
-       io_loop_destroy(&ioloop);
-
-       if (debug)
-               i_debug("Terminated");
-
-       i_close_fd(&fd_listen);
-       return 0;
-}
-
-static void test_run_client(test_client_init_t *client_test)
-{
-       i_set_failure_prefix("CLIENT: ");
-
-       if (debug)
-               i_debug("PID=%s", my_pid);
-
-       i_sleep_intr_msecs(100); /* wait a little for server setup */
-
-       ioloop = io_loop_create();
-       if (client_test())
-               io_loop_run(ioloop);
-       test_client_deinit();
-       io_loop_destroy(&ioloop);
-
-       if (debug)
-               i_debug("Terminated");
-}
-
-static void
-test_run_client_server(test_client_init_t *client_test,
-                      test_server_init_t *server_test)
-{
-       if (server_test != NULL) {
-               /* Fork server */
-               fd_listen = test_open_server_fd();
-               test_subprocess_fork(test_run_server, server_test, FALSE);
-               i_close_fd(&fd_listen);
-       }
-
-       /* Run client */
-       test_run_client(client_test);
-
-       i_unset_failure_prefix();
-       test_subprocess_kill_all(SERVER_KILL_TIMEOUT_SECS);
-}
-
-/*
- * Main
- */
-
-static void main_cleanup(void)
-{
-       i_unlink_if_exists(TEST_SOCKET);
-}
-
-static void main_init(void)
-{
-       /* nothing yet */
-}
-
-static void main_deinit(void)
-{
-       /* nothing yet; also called from sub-processes */
-}
-
-int main(int argc, char *argv[])
-{
-       int c;
-       int ret;
-
-       lib_init();
-       main_init();
-
-       while ((c = getopt(argc, argv, "D")) > 0) {
-               switch (c) {
-               case 'D':
-                       debug = TRUE;
-                       break;
-               default:
-                       i_fatal("Usage: %s [-D]", argv[0]);
-               }
-       }
-
-       test_subprocesses_init(debug);
-       test_subprocess_set_cleanup_callback(main_cleanup);
-
-       ret = test_run(test_functions);
-
-       test_subprocesses_deinit();
-       main_deinit();
-       lib_deinit();
-       return ret;
-}