X-Git-Url: https://git.tokkee.org/?p=sysdb.git;a=blobdiff_plain;f=t%2Funit%2Ffrontend%2Fconnection_test.c;h=9b0cde48da0ca11e725729fd01fe46318bc22d89;hp=125bb56fffb9eb2c3d8f6753fcf5e9fd4b50644f;hb=ad77ec3d257ac66d74e91452d098882247abdc0a;hpb=cf17313d62dcf1c10df0fe19287e354ede68206b diff --git a/t/unit/frontend/connection_test.c b/t/unit/frontend/connection_test.c index 125bb56..9b0cde4 100644 --- a/t/unit/frontend/connection_test.c +++ b/t/unit/frontend/connection_test.c @@ -31,10 +31,12 @@ #include "frontend/connection.h" #include "frontend/connection-private.h" +#include "utils/os.h" #include "libsysdb_test.h" #include "utils/strbuf.h" +#include #include #include @@ -47,6 +49,8 @@ #include #include +static char username[1024]; + /* * private helper functions */ @@ -60,9 +64,23 @@ mock_conn_destroy(sdb_conn_t *conn) sdb_strbuf_destroy(conn->errbuf); if (conn->fd >= 0) close(conn->fd); + if (conn->username) + free(conn->username); free(conn); } /* mock_conn_destroy */ +static ssize_t +mock_conn_read(sdb_conn_t *conn, size_t len) +{ + return sdb_strbuf_read(conn->buf, conn->fd, len); +} /* conn_read */ + +static ssize_t +mock_conn_write(sdb_conn_t *conn, const void *buf, size_t len) +{ + return sdb_write(conn->fd, len, buf); +} /* conn_write */ + static sdb_conn_t * mock_conn_create(void) { @@ -96,7 +114,13 @@ mock_conn_create(void) unlink(tmp_file); - conn->cmd = CONNECTION_IDLE; + conn->read = mock_conn_read; + conn->write = mock_conn_write; + + conn->username = strdup(username); + assert(conn->username); + + conn->cmd = SDB_CONNECTION_IDLE; conn->cmd_len = 0; return conn; } /* mock_conn_create */ @@ -110,28 +134,27 @@ mock_conn_rewind(sdb_conn_t *conn) static void mock_conn_truncate(sdb_conn_t *conn) { + int status; lseek(conn->fd, 0, SEEK_SET); - ftruncate(conn->fd, 0); + status = ftruncate(conn->fd, 0); + fail_unless(status == 0, + "INTERNAL ERROR: ftruncate(%d, 0) = %d; expected: 0", + conn->fd, status); } /* mock_conn_truncate */ static int -mock_unixsock_listener(char *sock_path) +mock_unixsock_listener(char *socket_path) { struct sockaddr_un sa; - char *filename; int fd, status; - filename = tmpnam(sock_path); - fail_unless(filename != NULL, - "INTERNAL ERROR: tmpnam() = NULL; expected: a string"); - fd = socket(AF_UNIX, SOCK_STREAM, 0); fail_unless(fd >= 0, "INTERNAL ERROR: socket() = %d; expected: >=0", fd); memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_UNIX; - strncpy(sa.sun_path, filename, sizeof(sa.sun_path)); + strncpy(sa.sun_path, socket_path, sizeof(sa.sun_path)); status = bind(fd, (struct sockaddr *)&sa, sizeof(sa)); fail_unless(status == 0, @@ -167,13 +190,39 @@ mock_client(void *arg) return NULL; } /* mock_client */ +static void +connection_startup(sdb_conn_t *conn) +{ + ssize_t check, expected; + + expected = 2 * sizeof(uint32_t) + strlen(username); + check = sdb_connection_send(conn, SDB_CONNECTION_STARTUP, + (uint32_t)strlen(username), username); + fail_unless(check == expected, + "sdb_connection_send(STARTUP, %s) = %zi; expected: %zi", + username, check, expected); + + mock_conn_rewind(conn); + check = sdb_connection_handle(conn); + fail_unless(check == expected, + "On startup: sdb_connection_handle() = %zi; expected: %zi", + check, expected); + + fail_unless(sdb_strbuf_len(conn->errbuf) == 0, + "sdb_connection_handle() left %zu bytes in the error " + "buffer (%s); expected: 0", sdb_strbuf_len(conn->errbuf), + sdb_strbuf_string(conn->errbuf)); + + mock_conn_truncate(conn); +} /* connection_startup */ + /* * tests */ START_TEST(test_conn_accept) { - char socket_path[L_tmpnam]; + char socket_path[] = "connection_test_socket.XXXXXX"; int fd, check; sdb_conn_t *conn; @@ -184,7 +233,10 @@ START_TEST(test_conn_accept) fail_unless(conn == NULL, "sdb_connection_accept(-1) = %p; expected: NULL", conn); - memset(&socket_path, 0, sizeof(socket_path)); + fd = mkstemp(socket_path); + unlink(socket_path); + close(fd); + fd = mock_unixsock_listener(socket_path); check = pthread_create(&thr, /* attr = */ NULL, mock_client, socket_path); fail_unless(check == 0, @@ -196,10 +248,12 @@ START_TEST(test_conn_accept) unlink(socket_path); sdb_connection_close(conn); + sdb_object_deref(SDB_OBJ(conn)); pthread_join(thr, NULL); } END_TEST +/* test connection setup and very basic commands */ START_TEST(test_conn_setup) { sdb_conn_t *conn = mock_conn_create(); @@ -209,14 +263,15 @@ START_TEST(test_conn_setup) const char *msg; const char *err; } golden_data[] = { - { UINT32_MAX, NULL, NULL }, - { CONNECTION_IDLE, "fakedata", NULL }, - { CONNECTION_PING, NULL, "Authentication required" }, - { CONNECTION_STARTUP, "fakeuser", NULL }, - { CONNECTION_PING, NULL, NULL }, - { CONNECTION_IDLE, NULL, NULL }, - { CONNECTION_PING, "fakedata", NULL }, - { CONNECTION_IDLE, NULL, NULL }, + /* code == UINT32_MAX => no data will be sent */ + { UINT32_MAX, NULL, NULL }, + { SDB_CONNECTION_IDLE, "fakedata", "Authentication required" }, + { SDB_CONNECTION_PING, NULL, "Authentication required" }, + { SDB_CONNECTION_STARTUP, username, NULL }, + { SDB_CONNECTION_PING, NULL, NULL }, + { SDB_CONNECTION_IDLE, NULL, "Invalid command 0" }, + { SDB_CONNECTION_PING, "fakedata", NULL }, + { SDB_CONNECTION_IDLE, NULL, "Invalid command 0" }, }; size_t i; @@ -242,24 +297,138 @@ START_TEST(test_conn_setup) } mock_conn_rewind(conn); - check = sdb_connection_read(conn); + check = sdb_connection_handle(conn); fail_unless(check == expected, - "sdb_connection_read() = %zi; expected: %zi", + "sdb_connection_handle() = %zi; expected: %zi", check, expected); fail_unless(sdb_strbuf_len(conn->buf) == 0, - "sdb_connection_read() left %zu bytes in the buffer; " + "sdb_connection_handle() left %zu bytes in the buffer; " "expected: 0", sdb_strbuf_len(conn->buf)); if (golden_data[i].err) { const char *err = sdb_strbuf_string(conn->errbuf); fail_unless(strcmp(err, golden_data[i].err) == 0, - "sdb_connection_read(): got error '%s'; " + "sdb_connection_handle(): got error '%s'; " "expected: '%s'", err, golden_data[i].err); } else fail_unless(sdb_strbuf_len(conn->errbuf) == 0, - "sdb_connection_read() left %zu bytes in the error " + "sdb_connection_handle() left %zu bytes in the error " + "buffer (%s); expected: 0", sdb_strbuf_len(conn->errbuf), + sdb_strbuf_string(conn->errbuf)); + } + + mock_conn_destroy(conn); +} +END_TEST + +/* test simple I/O on open connections */ +START_TEST(test_conn_io) +{ + sdb_conn_t *conn = mock_conn_create(); + + struct { + uint32_t code; + uint32_t msg_len; + const char *msg; + size_t buf_len; /* number of bytes we expect in conn->buf */ + const char *err; + } golden_data[] = { + /* code == UINT32_MAX => this is a follow-up package */ + { SDB_CONNECTION_PING, 20, "9876543210", 0, "Authentication required" }, + { UINT32_MAX, -1, "9876543210", 0, "Authentication required" }, + { SDB_CONNECTION_PING, 10, "9876543210", 0, "Authentication required" }, + { SDB_CONNECTION_IDLE, 10, "9876543210", 0, "Authentication required" }, + { SDB_CONNECTION_IDLE, 20, "9876543210", 0, "Authentication required" }, + { UINT32_MAX, -1, "9876543210", 0, "Authentication required" }, + { SDB_CONNECTION_STARTUP, -1, NULL, 0, NULL }, + { SDB_CONNECTION_PING, 20, "9876543210", 10, NULL }, + { UINT32_MAX, -1, "9876543210", 0, NULL }, + { SDB_CONNECTION_IDLE, 20, "9876543210", 0, "Invalid command 0" }, + { UINT32_MAX, -1, "9876543210", 0, "Invalid command 0" }, + { SDB_CONNECTION_IDLE, 20, "9876543210", 0, "Invalid command 0" }, + { UINT32_MAX, -1, "9876543210", 0, "Invalid command 0" }, + { SDB_CONNECTION_PING, 10, "9876543210", 0, NULL }, + { SDB_CONNECTION_PING, 20, "9876543210", 10, NULL }, + { UINT32_MAX, -1, "9876543210", 0, NULL }, + }; + + size_t i; + + for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) { + size_t msg_len = golden_data[i].msg ? strlen(golden_data[i].msg) : 0; + char buffer[2 * sizeof(uint32_t) + msg_len]; + size_t offset = 0; + + ssize_t check; + + mock_conn_truncate(conn); + + if (golden_data[i].code == SDB_CONNECTION_STARTUP) { + connection_startup(conn); + continue; + } + + if (golden_data[i].code != UINT32_MAX) { + uint32_t tmp; + + tmp = htonl(golden_data[i].code); + memcpy(buffer, &tmp, sizeof(tmp)); + tmp = htonl(golden_data[i].msg_len); + memcpy(buffer + sizeof(tmp), &tmp, sizeof(tmp)); + + msg_len += 2 * sizeof(uint32_t); + offset += 2 * sizeof(uint32_t); + } + + memcpy(buffer + offset, golden_data[i].msg, + strlen(golden_data[i].msg)); + + check = sdb_write(conn->fd, msg_len, buffer); + fail_unless(check == (ssize_t)msg_len, + "sdb_write(%s) = %zi; expected: %zu", + check, msg_len); + + mock_conn_rewind(conn); + check = sdb_connection_handle(conn); + fail_unless(check == (ssize_t)msg_len, + "sdb_connection_handle() = %zi; expected: %zu", + check, msg_len); + + if (golden_data[i].buf_len) { + /* partial commands need to be stored in the object */ + fail_unless(conn->cmd == golden_data[i].code, + "sdb_connection_handle() set partial command " + "to %u; expected: %u", conn->cmd, golden_data[i].code); + fail_unless(conn->cmd_len > golden_data[i].buf_len, + "sdb_connection_handle() set partial command length " + "to %u; expected: > %u", conn->cmd_len, + golden_data[i].buf_len); + } + else { + fail_unless(conn->cmd == SDB_CONNECTION_IDLE, + "sdb_connection_handle() did not reset command; " + "got %u; expected: %u", conn->cmd, SDB_CONNECTION_IDLE); + fail_unless(conn->cmd_len == 0, + "sdb_connection_handle() did not reset command length; " + "got %u; expected: 0", conn->cmd_len); + } + + fail_unless(sdb_strbuf_len(conn->buf) == golden_data[i].buf_len, + "sdb_connection_handle() left %zu bytes in the buffer; " + "expected: %zu", sdb_strbuf_len(conn->buf), + golden_data[i].buf_len); + + if (golden_data[i].err) { + const char *err = sdb_strbuf_string(conn->errbuf); + fail_unless(strcmp(err, golden_data[i].err) == 0, + "sdb_connection_handle(): got error '%s'; " + "expected: '%s'", err, golden_data[i].err); + } + else + fail_unless(sdb_strbuf_len(conn->errbuf) == 0, + "sdb_connection_handle() left %zu bytes in the error " "buffer; expected: 0", sdb_strbuf_len(conn->errbuf)); } @@ -273,9 +442,15 @@ fe_conn_suite(void) Suite *s = suite_create("frontend::connection"); TCase *tc; + char *tmp = sdb_get_current_user(); + assert(tmp); + strcpy(username, tmp); + free(tmp); + tc = tcase_create("core"); tcase_add_test(tc, test_conn_accept); tcase_add_test(tc, test_conn_setup); + tcase_add_test(tc, test_conn_io); suite_add_tcase(s, tc); return s;