From 6cf053c9df27179190541c126a57c8a8ceb6bf9d Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Tue, 1 Mar 2022 14:33:35 +0000 Subject: [PATCH] Add libipm test suite --- common/os_calls.c | 11 + common/os_calls.h | 1 + configure.ac | 1 + tests/Makefile.am | 1 + tests/libipm/Makefile.am | 28 + tests/libipm/test_libipm.h | 60 ++ tests/libipm/test_libipm_main.c | 194 ++++++ tests/libipm/test_libipm_recv_calls.c | 857 ++++++++++++++++++++++++++ tests/libipm/test_libipm_send_calls.c | 506 +++++++++++++++ 9 files changed, 1659 insertions(+) create mode 100644 tests/libipm/Makefile.am create mode 100644 tests/libipm/test_libipm.h create mode 100644 tests/libipm/test_libipm_main.c create mode 100644 tests/libipm/test_libipm_recv_calls.c create mode 100644 tests/libipm/test_libipm_send_calls.c diff --git a/common/os_calls.c b/common/os_calls.c index e7dea20e..2fdbea4a 100644 --- a/common/os_calls.c +++ b/common/os_calls.c @@ -587,6 +587,17 @@ g_sck_local_socket(void) #endif } +/*****************************************************************************/ +int +g_sck_local_socketpair(int sck[2]) +{ +#if defined(_WIN32) + return -1; +#else + return socketpair(PF_LOCAL, SOCK_STREAM, 0, sck); +#endif +} + /*****************************************************************************/ int g_sck_vsock_socket(void) diff --git a/common/os_calls.h b/common/os_calls.h index 3e254790..3511d7cc 100644 --- a/common/os_calls.h +++ b/common/os_calls.h @@ -71,6 +71,7 @@ int g_sck_get_send_buffer_bytes(int sck, int *bytes); int g_sck_set_recv_buffer_bytes(int sck, int bytes); int g_sck_get_recv_buffer_bytes(int sck, int *bytes); int g_sck_local_socket(void); +int g_sck_local_socketpair(int sck[2]); int g_sck_vsock_socket(void); int g_sck_get_peer_cred(int sck, int *pid, int *uid, int *gid); void g_sck_close(int sck); diff --git a/configure.ac b/configure.ac index 5f943b2f..6af725bc 100644 --- a/configure.ac +++ b/configure.ac @@ -498,6 +498,7 @@ AC_CONFIG_FILES([ sesman/tools/Makefile tests/Makefile tests/common/Makefile + tests/libipm/Makefile tests/libxrdp/Makefile tests/memtest/Makefile tests/xrdp/Makefile diff --git a/tests/Makefile.am b/tests/Makefile.am index 01ed83f2..861d0af6 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -4,6 +4,7 @@ EXTRA_DIST = \ SUBDIRS = \ common \ + libipm \ libxrdp \ memtest \ xrdp diff --git a/tests/libipm/Makefile.am b/tests/libipm/Makefile.am new file mode 100644 index 00000000..76795d8d --- /dev/null +++ b/tests/libipm/Makefile.am @@ -0,0 +1,28 @@ + +AM_CPPFLAGS = \ + -I$(top_builddir) \ + -I$(top_srcdir)/libipm \ + -I$(top_srcdir)/common + +LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \ + $(top_srcdir)/tap-driver.sh + +PACKAGE_STRING = "libipm" + +TESTS = test_libipm +check_PROGRAMS = test_libipm + +test_libipm_SOURCES = \ + test_libipm_main.c \ + test_libipm.h \ + test_libipm_send_calls.c \ + test_libipm_recv_calls.c + +test_libipm_CFLAGS = \ + @CHECK_CFLAGS@ \ + -D TOP_SRCDIR=\"$(top_srcdir)\" + +test_libipm_LDADD = \ + $(top_builddir)/libipm/libipm.la \ + $(top_builddir)/common/libcommon.la \ + @CHECK_LIBS@ diff --git a/tests/libipm/test_libipm.h b/tests/libipm/test_libipm.h new file mode 100644 index 00000000..5105a1c1 --- /dev/null +++ b/tests/libipm/test_libipm.h @@ -0,0 +1,60 @@ + +#ifndef TEST_LIBIPM__H +#define TEST_LIBIPM__H + +#include + +/* Private libipm stuff. This is duplicated here, so if the libipm + * value change, tests will fail! */ +enum +{ + LIBIPM_VERSION = 2, + LIBIPM_HEADER_SIZE = 12, + LIBIPM_MAX_MESSAGE_SIZE = 8192, + LIBIPM_MAX_PAYLOAD_SIZE = LIBIPM_MAX_MESSAGE_SIZE - LIBIPM_HEADER_SIZE +}; + +/* Globals */ +extern struct trans *g_t_out; /* Channel for outputting libipm messages */ +extern struct trans *g_t_in; /* Channel for inputting libipm messages */ +extern struct trans *g_t_vanilla; /* Non-SCP channel */ + +extern const char *g_supported_types; /* All recognised type codes */ +extern const char *g_unimplemented_types; /* recognised, unimplemented codes */ + +#define TEST_MESSAGE_NO 66 +/* Test message with no string translation */ +#define TEST_MESSAGE_NO_STRING_NO 67 + +/** + * Compares two binary blocks + * + * @param actual_data Actual data + * @param actual_len Actual length + * @param expected_data Expected data + * @aram expected_len Expected data length + * + * Differences are raised using ck_* calls + */ +void +check_binary_data_eq(const void *actual_data, + unsigned int actual_len, + const void *expected_data, + unsigned int expected_len); + +/** + * Looks for the specified string in the specified stream + * @param s Stream to search + * @param str Null-terminated string to look for + * @return != 0 if string found in the buffer + * + * The whole buffer is searched for the string. not just the + * used bit. + */ +int +does_stream_contain_string(const struct stream *s, const char *str); + +Suite *make_suite_test_libipm_send_calls(void); +Suite *make_suite_test_libipm_recv_calls(void); + +#endif /* TEST_LIBIPM__H */ diff --git a/tests/libipm/test_libipm_main.c b/tests/libipm/test_libipm_main.c new file mode 100644 index 00000000..1ecfcaf0 --- /dev/null +++ b/tests/libipm/test_libipm_main.c @@ -0,0 +1,194 @@ + +#if defined(HAVE_CONFIG_H) +#include "config_ac.h" +#endif + +#include + +#include "log.h" +#include "libipm.h" +#include "trans.h" +#include "os_calls.h" +#include "string_calls.h" + +#include "test_libipm.h" + +struct trans *g_t_out = NULL; +struct trans *g_t_in = NULL; +struct trans *g_t_vanilla = NULL; + +const char *g_supported_types = "ybnqiuxtsdhogB"; +const char *g_unimplemented_types = "dhog"; + +/******************************************************************************/ +static const char * +msgno_to_str(unsigned short msgno) +{ + return (msgno == TEST_MESSAGE_NO) ? "TEST_MESSAGE_NO" : NULL; +} + +/******************************************************************************/ +static void +suite_test_libipm_calls_start(void) +{ + int sck[2]; + int istatus; + struct trans *t1 = NULL; + struct trans *t2 = NULL; + struct trans *t3 = NULL; + int success = 0; + + if ((t1 = trans_create(TRANS_MODE_UNIX, 128, 128)) == NULL) + { + const char *errstr = g_get_strerror(); + LOG(LOG_LEVEL_ERROR, "Can't create test transport 1 [%s]", errstr); + } + else if ((t2 = trans_create(TRANS_MODE_UNIX, 128, 128)) == NULL) + { + const char *errstr = g_get_strerror(); + LOG(LOG_LEVEL_ERROR, "Can't create test transport 2 [%s]", errstr); + } + else if ((t3 = trans_create(TRANS_MODE_UNIX, 128, 128)) == NULL) + { + const char *errstr = g_get_strerror(); + LOG(LOG_LEVEL_ERROR, "Can't create test transport 3 [%s]", errstr); + } + else if ((istatus = g_sck_local_socketpair(sck)) < 0) + { + const char *errstr = g_get_strerror(); + LOG(LOG_LEVEL_ERROR, "Can't create test sockets [%s]", errstr); + } + else + { + enum libipm_status init1; + enum libipm_status init2; + + g_sck_set_non_blocking(sck[1]); + + t1->sck = sck[0]; + t1->type1 = TRANS_TYPE_CLIENT; + t1->status = TRANS_STATUS_UP; + + t2->sck = sck[1]; + t2->type1 = TRANS_TYPE_SERVER; + t2->status = TRANS_STATUS_UP; + + init1 = libipm_init_trans(t1, LIBIPM_FAC_TEST, msgno_to_str); + init2 = libipm_init_trans(t2, LIBIPM_FAC_TEST, msgno_to_str); + if (init1 != E_LI_SUCCESS || init2 != E_LI_SUCCESS) + { + LOG(LOG_LEVEL_ERROR, "libipm_init_trans() call failed"); + } + else + { + success = 1; + } + } + + if (success) + { + g_t_out = t1; + g_t_in = t2; + g_t_vanilla = t3; + } + else + { + trans_delete(t1); + trans_delete(t2); + trans_delete(t3); + } +} + +/******************************************************************************/ +static void +suite_test_libipm_calls_end(void) +{ + trans_delete(g_t_out); + trans_delete(g_t_in); + trans_delete(g_t_vanilla); +} + +/******************************************************************************/ +void +check_binary_data_eq(const void *actual_data, + unsigned int actual_len, + const void *expected_data, + unsigned int expected_len) +{ + const unsigned char *cp_expected = (const unsigned char *)expected_data; + const unsigned char *cp_expected_end = + (const unsigned char *)expected_data + expected_len; + const unsigned char *cp_actual = (const unsigned char *)actual_data; + + if (expected_len != actual_len) + { + ck_abort_msg("Differing lengths. Expected %u, got %u", + expected_len, actual_len); + } + + while (cp_expected < cp_expected_end) + { + if (*cp_expected != *cp_actual) + { + int byte_pos = cp_expected - (const unsigned char *)expected_data; + ck_abort_msg("Byte position %d. Expected %02x, got %02x", + byte_pos, *cp_expected, *cp_actual); + } + ++cp_expected; + ++cp_actual; + } +} + +/******************************************************************************/ + +int +does_stream_contain_string(const struct stream *s, const char *str) +{ + int len = g_strlen(str); + int i; + if (len > 0 && len <= s->size) + { + for (i = 0 ; i <= s->size - len; ++i) + { + /* This is not a sophisticated string search. We use a + * single character test to avoid a function call for + * every comparison */ + if (str[0] == s->data[i] && g_memcmp(str, &s->data[i], len) == 0) + { + return 1; + } + } + } + + return 0; +} + + +/******************************************************************************/ +int main (void) +{ + int number_failed; + SRunner *sr; + + sr = srunner_create (make_suite_test_libipm_send_calls()); + srunner_add_suite (sr, make_suite_test_libipm_recv_calls()); + + srunner_set_tap(sr, "-"); + /* + * Set up console logging */ + struct log_config *lc = log_config_init_for_console(LOG_LEVEL_INFO, NULL); + log_start_from_param(lc); + log_config_free(lc); + + /* Initialise modules */ + suite_test_libipm_calls_start(); + + srunner_run_all (sr, CK_ENV); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + + suite_test_libipm_calls_end(); + + log_end(); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/tests/libipm/test_libipm_recv_calls.c b/tests/libipm/test_libipm_recv_calls.c new file mode 100644 index 00000000..67ba5e0f --- /dev/null +++ b/tests/libipm/test_libipm_recv_calls.c @@ -0,0 +1,857 @@ + +#if defined(HAVE_CONFIG_H) +#include "config_ac.h" +#endif + +#include + +#include "libipm.h" +#include "os_calls.h" +#include "string_calls.h" +#include "trans.h" + +#include "test_libipm.h" + +/* Random(ish) values for test_libipm_send_recv_all_test */ +#define TEST_y_VALUE 89 +#define TEST_b_VALUE 1 +#define TEST_n_VALUE -143 +#define TEST_q_VALUE 329 +#define TEST_i_VALUE -150000 +#define TEST_u_VALUE 150000 +#define TEST_x_VALUE -4500000000L +#define TEST_t_VALUE 8500000000L +#define TEST_s_VALUE "libipm recv test" +#define TEST_B_VALUE 'b', 'y', 'B', 'Y', '8', '2', '/' + +/** + * Type for fields in the message header + * + * The type value is the offset of the field within the header + */ +enum header_field +{ + HDR_IPM_VER = 0, + HDR_MSG_LEN = 2, + HDR_FACILITY = 4, + HDR_MSGNO = 6, + HDR_RESERVED = 8 +}; + +/** + * Gets a message header field value */ +static unsigned int +get_header_field(struct stream *s, enum header_field field) +{ + unsigned int res; + char *saved_p = s->p; + s->p = s->data + (unsigned short)field; + if (field == HDR_RESERVED) + { + in_uint32_le(s, res); + } + else + { + in_uint16_le(s, res); + } + + s->p = saved_p; + + return res; +} + +/** + * Sets a message header field value */ +static void +set_header_field(struct stream *s, enum header_field field, unsigned int val) +{ + char *saved_p = s->p; + s->p = s->data + (unsigned short)field; + if (field == HDR_RESERVED) + { + out_uint32_le(s, val); + } + else + { + out_uint16_le(s, val); + } + + s->p = saved_p; +} + +/** + * Flushes input on a non-blocking socket + * + * Returns number of bytes read + */ +static unsigned int +flush_socket(int sck) +{ + char buff[1024]; + unsigned int result = 0; + int status; + while (1) + { + status = g_sck_recv(g_t_in->sck, buff, sizeof(buff), 0); + if (status < 0) + { + break; + } + result += status; + } + + return result; +} + +/** + * Waits for an expected incoming message */ +static void +check_for_incoming_message(unsigned short expected_msgno) +{ + enum libipm_status status; + unsigned short msgno; + + /* Get the message at the other end */ + libipm_msg_in_reset(g_t_in); + status = libipm_msg_in_wait_available(g_t_in); + ck_assert_int_eq(status, E_LI_SUCCESS); + + msgno = libipm_msg_in_start(g_t_in); + ck_assert_int_eq(msgno, expected_msgno); +} + +/** + * Test for input truncated by a number of bytes + * + * This call assumes the output buffer contains a message of a single + * type that has already been successfully sent and checked. + * + * The send size is truncated, and the message is sent again. This + * lets us check the input parser won't accept a type for which + * there is insufficient data. + */ +static void +test_truncated_input_type(char typechar, void *valueptr, + int truncate_value, + enum libipm_status expected_status) + +{ + const char format[] = {typechar, '\0'}; + enum libipm_status status; + int istatus; + unsigned int msg_size; + + /* The same message is still in the buffer. + * + * reduce the payload length by the truncate value...*/ + msg_size = get_header_field(g_t_out->out_s, HDR_MSG_LEN); + set_header_field(g_t_out->out_s, HDR_MSG_LEN, msg_size - truncate_value); + + /* ...and re-send it */ + istatus = trans_force_write(g_t_out); + ck_assert_int_eq(istatus, 0); + + /* Catch the message at the other end */ + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Check the value */ + switch (typechar) + { + case 'B': + /* For this type, the descriptor is critical to read + * the value */ + status = libipm_msg_in_parse(g_t_in, format, + (struct fsb_type *)valueptr); + break; + default: + /* For other types, the value shouldn't be needed, as + * it's only written to if the parse is successful */ + status = libipm_msg_in_parse(g_t_in, format, + (struct fsb_type *)NULL); + break; + } + ck_assert_int_eq(status, expected_status); + + /* There should be 'truncate_value' extra octets left on the input + * socket which wasn't read when we shrunk the header */ + istatus = flush_socket(g_t_in->sck); + ck_assert_int_eq(istatus, truncate_value); + + /* Put the message size back to its original value, for further tests */ + set_header_field(g_t_out->out_s, HDR_MSG_LEN, msg_size); +} + +/** + * Test for bad header values + * + * This call assumes the output buffer contains a message containing + * a single 'u' type that has already been send and checked + * + * The specified header field is overwritten, and the message is + * sent. On the receive side we check for a 'bad header' error, and + * then put everything back to its starting place. + */ +static void +test_bad_header_value(enum header_field field, unsigned int test_val) +{ + unsigned int old_val; + enum libipm_status status; + int istatus; + + /* The same message is still in the buffer. + * + * Save the field value we are going to change, and replace it */ + old_val = get_header_field(g_t_out->out_s, field); + set_header_field(g_t_out->out_s, field, test_val); + + /* re-send the message */ + istatus = trans_force_write(g_t_out); + ck_assert_int_eq(istatus, 0); + + /* Catch the message at the other end. The error is + * reported when we wait for the incoming message */ + libipm_msg_in_reset(g_t_in); + status = libipm_msg_in_wait_available(g_t_in); + ck_assert_int_eq(status, E_LI_BAD_HEADER); + + /* There should be 5 extra octets ('u' + 32-bit value) left on the input + * socket which wasn't read when we broke the header */ + istatus = flush_socket(g_t_in->sck); + ck_assert_int_eq(istatus, 1 + sizeof(uint32_t)); + + /* Put the message size back to its original value, for further tests */ + set_header_field(g_t_out->out_s, field, old_val); +} + +/***************************************************************************//** + * As the 'append all' test, but data is sent across a link, demarshalled, + * and validated */ +START_TEST(test_libipm_send_recv_all_test) +{ + ck_assert_ptr_ne(g_t_out, NULL); + ck_assert_ptr_ne(g_t_in, NULL); + + static char bin_out[] = { TEST_B_VALUE }; + struct libipm_fsb binary_desc = { (void *)bin_out, sizeof(bin_out) }; + enum libipm_status status; + + /* variables for received values */ + uint8_t y; + int b; + int16_t n; + uint16_t q; + int32_t i; + uint32_t u; + int64_t x; + uint64_t t; + char *s; + char B[sizeof(bin_out)]; + char c; + + /* The message is small enough to fit in the socket buffer */ + status = libipm_msg_out_simple_send( + g_t_out, TEST_MESSAGE_NO, + "ybnqiuxtsB", + TEST_y_VALUE, + TEST_b_VALUE, + TEST_n_VALUE, + TEST_q_VALUE, + TEST_i_VALUE, + TEST_u_VALUE, + TEST_x_VALUE, + TEST_t_VALUE, + TEST_s_VALUE, + &binary_desc); + ck_assert_int_eq(status, E_LI_SUCCESS); + + /* Catch the message at the other end */ + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Re-use our descriptor for the receive operation */ + binary_desc.data = (void *)&B; + binary_desc.datalen = sizeof(B); + + status = libipm_msg_in_parse( + g_t_in, + "ybnqiuxtsB", + &y, &b, &n, &q, &i, &u, &x, &t, &s, &binary_desc); + + ck_assert_int_eq(status, E_LI_SUCCESS); + ck_assert_int_eq(y, TEST_y_VALUE); + ck_assert_int_eq(b, TEST_b_VALUE); + ck_assert_int_eq(n, TEST_n_VALUE); + ck_assert_int_eq(q, TEST_q_VALUE); + ck_assert_int_eq(i, TEST_i_VALUE); + ck_assert_int_eq(u, TEST_u_VALUE); + ck_assert_int_eq(x, TEST_x_VALUE); + ck_assert_int_eq(t, TEST_t_VALUE); + check_binary_data_eq(TEST_s_VALUE, sizeof(TEST_s_VALUE) - 1, + s, g_strlen(s)); + check_binary_data_eq(bin_out, sizeof(bin_out), B, sizeof(B)); + + /* Check we're at the end of the message */ + c = libipm_msg_in_peek_type(g_t_in); + ck_assert_int_eq(c, '\0'); +} +END_TEST + +/***************************************************************************//** + * Checks various receive errors for 'y' + */ +START_TEST(test_libipm_receive_y_type) +{ + enum libipm_status status; + uint8_t y; + + /* First, a simple send... */ + status = libipm_msg_out_simple_send( + g_t_out, TEST_MESSAGE_NO, "y", TEST_y_VALUE); + ck_assert_int_eq(status, E_LI_SUCCESS); + + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Check the value */ + status = libipm_msg_in_parse( g_t_in, "y", &y); + ck_assert_int_eq(status, E_LI_SUCCESS); + ck_assert_int_eq(y, TEST_y_VALUE); + + test_truncated_input_type('y', &y, 1, E_LI_BUFFER_OVERFLOW); +} +END_TEST + +/***************************************************************************//** + * Checks various receive errors for 'b' + */ +START_TEST(test_libipm_receive_b_type) +{ + enum libipm_status status; + int b; + int istatus; + char c; + + /* First, a simple send... */ + status = libipm_msg_out_simple_send( + g_t_out, TEST_MESSAGE_NO, "b", TEST_b_VALUE); + ck_assert_int_eq(status, E_LI_SUCCESS); + + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Check the value */ + status = libipm_msg_in_parse( g_t_in, "b", &b); + ck_assert_int_eq(status, E_LI_SUCCESS); + ck_assert_int_eq(b, TEST_b_VALUE); + + test_truncated_input_type('b', &b, 1, E_LI_BUFFER_OVERFLOW); + + /* Resend and re-catch the message */ + istatus = trans_force_write(g_t_out); + ck_assert_int_eq(istatus, 0); + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Modify the message to contain a '2' for the boolean */ + c = libipm_msg_in_peek_type(g_t_in); + ck_assert_int_eq(c, 'b'); /* Next type should be a 'b' */ + c = *(g_t_in->in_s->p + 1); /* This should be the test value */ + ck_assert_int_eq(c, TEST_b_VALUE); /* Check it */ + *(g_t_in->in_s->p + 1) = 2; + status = libipm_msg_in_parse( g_t_in, "b", &b); + ck_assert_int_eq(status, E_LI_BAD_VALUE); + + /* Resend and re-catch the message */ + istatus = trans_force_write(g_t_out); + ck_assert_int_eq(istatus, 0); + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Modify the message to contain a '-1' for the boolean */ + *(g_t_in->in_s->p + 1) = (char) -1; + status = libipm_msg_in_parse( g_t_in, "b", &b); + ck_assert_int_eq(status, E_LI_BAD_VALUE); +} +END_TEST + +/***************************************************************************//** + * Checks various receive errors for 'n' + */ +START_TEST(test_libipm_receive_n_type) +{ + enum libipm_status status; + int16_t n; + + /* First, a simple send... */ + status = libipm_msg_out_simple_send( + g_t_out, TEST_MESSAGE_NO, "n", TEST_n_VALUE); + ck_assert_int_eq(status, E_LI_SUCCESS); + + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Check the value */ + status = libipm_msg_in_parse( g_t_in, "n", &n); + ck_assert_int_eq(status, E_LI_SUCCESS); + ck_assert_int_eq(n, TEST_n_VALUE); + + test_truncated_input_type('n', &n, 1, E_LI_BUFFER_OVERFLOW); +} +END_TEST + +/***************************************************************************//** + * Checks various receive errors for 'q' + */ +START_TEST(test_libipm_receive_q_type) +{ + enum libipm_status status; + uint16_t q; + + /* First, a simple send... */ + status = libipm_msg_out_simple_send( + g_t_out, TEST_MESSAGE_NO, "q", TEST_q_VALUE); + ck_assert_int_eq(status, E_LI_SUCCESS); + + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Check the value */ + status = libipm_msg_in_parse( g_t_in, "q", &q); + ck_assert_int_eq(status, E_LI_SUCCESS); + ck_assert_int_eq(q, TEST_q_VALUE); + + test_truncated_input_type('q', &q, 1, E_LI_BUFFER_OVERFLOW); +} +END_TEST + +/***************************************************************************//** + * Checks various receive errors for 'i' + */ +START_TEST(test_libipm_receive_i_type) +{ + enum libipm_status status; + int32_t i; + + /* First, a simple send... */ + status = libipm_msg_out_simple_send( + g_t_out, TEST_MESSAGE_NO, "i", TEST_i_VALUE); + ck_assert_int_eq(status, E_LI_SUCCESS); + + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Check the value */ + status = libipm_msg_in_parse( g_t_in, "i", &i); + ck_assert_int_eq(status, E_LI_SUCCESS); + ck_assert_int_eq(i, TEST_i_VALUE); + + test_truncated_input_type('i', &i, 1, E_LI_BUFFER_OVERFLOW); +} +END_TEST + +/***************************************************************************//** + * Checks various receive errors for 'u' + */ +START_TEST(test_libipm_receive_u_type) +{ + enum libipm_status status; + uint32_t u; + + /* First, a simple send... */ + status = libipm_msg_out_simple_send( + g_t_out, TEST_MESSAGE_NO, "u", TEST_u_VALUE); + ck_assert_int_eq(status, E_LI_SUCCESS); + + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Check the value */ + status = libipm_msg_in_parse( g_t_in, "u", &u); + ck_assert_int_eq(status, E_LI_SUCCESS); + ck_assert_int_eq(u, TEST_u_VALUE); + + test_truncated_input_type('u', &u, 1, E_LI_BUFFER_OVERFLOW); +} +END_TEST + +/***************************************************************************//** + * Checks various receive errors for 'x' + */ +START_TEST(test_libipm_receive_x_type) +{ + enum libipm_status status; + int64_t x; + + /* First, a simple send... */ + status = libipm_msg_out_simple_send( + g_t_out, TEST_MESSAGE_NO, "x", TEST_x_VALUE); + ck_assert_int_eq(status, E_LI_SUCCESS); + + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Check the value */ + status = libipm_msg_in_parse( g_t_in, "x", &x); + ck_assert_int_eq(status, E_LI_SUCCESS); + ck_assert_int_eq(x, TEST_x_VALUE); + + test_truncated_input_type('x', &x, 1, E_LI_BUFFER_OVERFLOW); +} +END_TEST + +/***************************************************************************//** + * Checks various receive errors for 't' + */ +START_TEST(test_libipm_receive_t_type) +{ + enum libipm_status status; + uint64_t t; + + /* First, a simple send... */ + status = libipm_msg_out_simple_send( + g_t_out, TEST_MESSAGE_NO, "t", TEST_t_VALUE); + ck_assert_int_eq(status, E_LI_SUCCESS); + + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Check the value */ + status = libipm_msg_in_parse( g_t_in, "t", &t); + ck_assert_int_eq(status, E_LI_SUCCESS); + ck_assert_int_eq(t, TEST_t_VALUE); + + test_truncated_input_type('t', &t, 1, E_LI_BUFFER_OVERFLOW); +} +END_TEST + +/***************************************************************************//** + * Checks various receive errors for 's' + */ +START_TEST(test_libipm_receive_s_type) +{ + enum libipm_status status; + char *s; + + /* First, a simple send... */ + status = libipm_msg_out_simple_send( + g_t_out, TEST_MESSAGE_NO, "s", TEST_s_VALUE); + ck_assert_int_eq(status, E_LI_SUCCESS); + + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Check the value */ + status = libipm_msg_in_parse( g_t_in, "s", &s); + ck_assert_int_eq(status, E_LI_SUCCESS); + check_binary_data_eq(TEST_s_VALUE, sizeof(TEST_s_VALUE) - 1, + s, g_strlen(s)); + + /* This effectively tests that unterminated strings are not + * passed back to the user */ + test_truncated_input_type('s', &s, 1, E_LI_BAD_VALUE); +} +END_TEST + +/***************************************************************************//** + * Checks various receive errors for 's' + */ +START_TEST(test_libipm_receive_B_type) +{ + enum libipm_status status; + static char bin_out[] = { TEST_B_VALUE }; + char bin_in[sizeof(bin_out)]; + + struct libipm_fsb binary_desc = { (void *)bin_out, sizeof(bin_out) }; + + /* First, a simple send... */ + status = libipm_msg_out_simple_send( + g_t_out, TEST_MESSAGE_NO, "B", &binary_desc); + ck_assert_int_eq(status, E_LI_SUCCESS); + + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Check the value */ + binary_desc.data = (void *)bin_in; + binary_desc.datalen = sizeof(bin_in); + + status = libipm_msg_in_parse( g_t_in, "B", &binary_desc); + ck_assert_int_eq(status, E_LI_SUCCESS); + check_binary_data_eq(bin_out, sizeof(bin_out), + bin_in, sizeof(bin_in)); + + /* Check a truncated binary object is rejected */ + test_truncated_input_type('B', &binary_desc, 1, E_LI_BUFFER_OVERFLOW); + + /* Check a binary object without a complete length field is rejected */ + test_truncated_input_type('B', &binary_desc, sizeof(bin_out) + 1, + E_LI_BUFFER_OVERFLOW); + + /* Check a binary object with a mismatching FSB length field is rejected */ + --binary_desc.datalen; + test_truncated_input_type('B', &binary_desc, 0, E_LI_BAD_VALUE); + ++binary_desc.datalen; + + /* Check a binary object with a null FSB data field is rejected */ + binary_desc.data = NULL; + test_truncated_input_type('B', &binary_desc, 0, E_LI_PROGRAM_ERROR); + test_truncated_input_type('B', NULL, 0, E_LI_PROGRAM_ERROR); +} +END_TEST + + +/***************************************************************************//** + * Checks for a message with a completely missing type + */ +START_TEST(test_libipm_receive_no_type) +{ + enum libipm_status status; + uint32_t u; + + /* First, a simple send... */ + status = libipm_msg_out_simple_send( + g_t_out, TEST_MESSAGE_NO, "u", TEST_u_VALUE); + ck_assert_int_eq(status, E_LI_SUCCESS); + + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Check the value */ + status = libipm_msg_in_parse( g_t_in, "u", &u); + ck_assert_int_eq(status, E_LI_SUCCESS); + ck_assert_int_eq(u, TEST_u_VALUE); + + /* Completely remove the type flag and the value from the message */ + test_truncated_input_type('u', &u, sizeof(uint32_t) + 1, + E_LI_BUFFER_OVERFLOW); +} +END_TEST + +/***************************************************************************//** + * Checks for a message with an unexpected type + */ +START_TEST(test_libipm_receive_unexpected_type) +{ + enum libipm_status status; + int istatus; + uint32_t u; + int32_t i; + + /* First, a simple send... */ + status = libipm_msg_out_simple_send( + g_t_out, TEST_MESSAGE_NO, "u", TEST_u_VALUE); + ck_assert_int_eq(status, E_LI_SUCCESS); + + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Check the value */ + status = libipm_msg_in_parse( g_t_in, "u", &u); + ck_assert_int_eq(status, E_LI_SUCCESS); + ck_assert_int_eq(u, TEST_u_VALUE); + + /* Resend and re-catch the message */ + istatus = trans_force_write(g_t_out); + ck_assert_int_eq(istatus, 0); + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Try parsing with a type mismatch */ + status = libipm_msg_in_parse( g_t_in, "i", &i); + ck_assert_int_eq(status, E_LI_UNEXPECTED_TYPE); +} +END_TEST + +/***************************************************************************//** + * Checks for a message with an unsupported type + */ +START_TEST(test_libipm_receive_unsupported_type) +{ + enum libipm_status status; + int istatus; + uint32_t u; + char c; + + /* First, a simple send... */ + status = libipm_msg_out_simple_send( + g_t_out, TEST_MESSAGE_NO, "u", TEST_u_VALUE); + ck_assert_int_eq(status, E_LI_SUCCESS); + + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Check the value */ + status = libipm_msg_in_parse( g_t_in, "u", &u); + ck_assert_int_eq(status, E_LI_SUCCESS); + ck_assert_int_eq(u, TEST_u_VALUE); + + /* Resend and re-catch the message */ + istatus = trans_force_write(g_t_out); + ck_assert_int_eq(istatus, 0); + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Modify the message to contain an unsupported type */ + c = libipm_msg_in_peek_type(g_t_in); + ck_assert_int_eq(c, 'u'); /* Next type should be a 'u' */ + *g_t_in->in_s->p = 'A'; /* unsupported type */ + c = libipm_msg_in_peek_type(g_t_in); + ck_assert_int_eq(c, '?'); /* peek should say this is an error */ + + status = libipm_msg_in_parse( g_t_in, "A", NULL); /* Parse it anyway */ + ck_assert_int_eq(status, E_LI_UNSUPPORTED_TYPE); +} +END_TEST + + +/***************************************************************************//** + * Checks for a message with an unimplemented type + */ +START_TEST(test_libipm_receive_unimplemented_type) +{ + enum libipm_status status; + int istatus; + uint32_t u; + char c; + + /* First, a simple send... */ + status = libipm_msg_out_simple_send( + g_t_out, TEST_MESSAGE_NO_STRING_NO, "u", TEST_u_VALUE); + ck_assert_int_eq(status, E_LI_SUCCESS); + + check_for_incoming_message(TEST_MESSAGE_NO_STRING_NO); + + /* Check the value */ + status = libipm_msg_in_parse( g_t_in, "u", &u); + ck_assert_int_eq(status, E_LI_SUCCESS); + ck_assert_int_eq(u, TEST_u_VALUE); + + /* Resend and re-catch the message */ + istatus = trans_force_write(g_t_out); + ck_assert_int_eq(istatus, 0); + check_for_incoming_message(TEST_MESSAGE_NO_STRING_NO); + + /* Try parsing with an unimplemented type. + * To do this we need to modify the type marker in the input stream */ + c = libipm_msg_in_peek_type(g_t_in); + ck_assert_int_eq(c, 'u'); /* Next type should be a 'u' */ + *g_t_in->in_s->p = 'd'; /* reserved type */ + c = libipm_msg_in_peek_type(g_t_in); + ck_assert_int_eq(c, 'd'); + status = libipm_msg_in_parse( g_t_in, "d", NULL); + ck_assert_int_eq(status, E_LI_UNIMPLEMENTED_TYPE); +} +END_TEST + +/***************************************************************************//** + * Checks for bad header values + */ +START_TEST(test_libipm_receive_bad_header) +{ + enum libipm_status status; + uint32_t u; + unsigned short hdr_ipm_ver; + unsigned short hdr_facility; + unsigned int i; + + /* First, a simple send... */ + status = libipm_msg_out_simple_send( + g_t_out, TEST_MESSAGE_NO, "u", TEST_u_VALUE); + ck_assert_int_eq(status, E_LI_SUCCESS); + + check_for_incoming_message(TEST_MESSAGE_NO); + + /* Check the value */ + status = libipm_msg_in_parse( g_t_in, "u", &u); + ck_assert_int_eq(status, E_LI_SUCCESS); + ck_assert_int_eq(u, TEST_u_VALUE); + + /* Save existing header values */ + hdr_ipm_ver = get_header_field(g_t_out->out_s, HDR_IPM_VER); + hdr_facility = get_header_field(g_t_out->out_s, HDR_FACILITY); + + test_bad_header_value(HDR_IPM_VER, hdr_ipm_ver + 1); + test_bad_header_value(HDR_FACILITY, hdr_facility - 1); + test_bad_header_value(HDR_RESERVED, 0xff); + test_bad_header_value(HDR_MSG_LEN, LIBIPM_MAX_MESSAGE_SIZE + 1); + test_bad_header_value(HDR_MSG_LEN, 0xffff); + for (i = 0 ; i < LIBIPM_HEADER_SIZE; ++i) + { + test_bad_header_value(HDR_MSG_LEN, i); + } +} +END_TEST + + +/***************************************************************************//** + * Checks message erase works as expected + */ +START_TEST(test_libipm_receive_msg_erase) +{ + enum libipm_status status; + int istatus; + const char *username = "username"; + const char *password = "password"; + + /* Send a message containing sensitive information */ + status = libipm_msg_out_simple_send(g_t_out, TEST_MESSAGE_NO, "ss", + username, password); + ck_assert_int_eq(status, E_LI_SUCCESS); + + check_for_incoming_message(TEST_MESSAGE_NO); + libipm_set_flags(g_t_in, LIBIPM_E_MSG_IN_ERASE_AFTER_USE); + + ck_assert_int_ne(does_stream_contain_string(g_t_in->in_s, password), 0); + libipm_msg_in_reset(g_t_in); + ck_assert_msg(does_stream_contain_string(g_t_in->in_s, password) == 0, + "Auto buffer reset on input not working"); + + /* The flag should be reset automatically */ + /* Resend and re-catch the message */ + istatus = trans_force_write(g_t_out); + ck_assert_int_eq(istatus, 0); + check_for_incoming_message(TEST_MESSAGE_NO); + + ck_assert_int_ne(does_stream_contain_string(g_t_in->in_s, password), 0); + libipm_msg_in_reset(g_t_in); + ck_assert_msg(does_stream_contain_string(g_t_in->in_s, password) != 0, + "LIBIPM_E_MSG_IN_ERASE_AFTER_USE not automatically cleared"); +} +END_TEST +/***************************************************************************//** + * Exercises codepaths that shouldn't be called (programming errors) + */ +START_TEST(test_libipm_receive_programming_errors) +{ + enum libipm_status status; + int available; + int32_t dummy; + + status = libipm_msg_in_wait_available(g_t_vanilla); + ck_assert_int_eq(status, E_LI_PROGRAM_ERROR); + + status = libipm_msg_in_check_available(g_t_vanilla, &available); + ck_assert_int_eq(status, E_LI_PROGRAM_ERROR); + + status = libipm_msg_in_parse(g_t_vanilla, "i", &dummy); + ck_assert_int_eq(status, E_LI_PROGRAM_ERROR); + + libipm_msg_in_reset(g_t_vanilla); /* No status to check */ + +} +END_TEST + +/******************************************************************************/ +Suite * +make_suite_test_libipm_recv_calls(void) +{ + Suite *s; + TCase *tc; + + s = suite_create("libipm_recv"); + + tc = tcase_create("libipm_recv"); + suite_add_tcase(s, tc); + tcase_add_test(tc, test_libipm_send_recv_all_test); + tcase_add_test(tc, test_libipm_receive_y_type); + tcase_add_test(tc, test_libipm_receive_b_type); + tcase_add_test(tc, test_libipm_receive_n_type); + tcase_add_test(tc, test_libipm_receive_q_type); + tcase_add_test(tc, test_libipm_receive_i_type); + tcase_add_test(tc, test_libipm_receive_u_type); + tcase_add_test(tc, test_libipm_receive_x_type); + tcase_add_test(tc, test_libipm_receive_t_type); + tcase_add_test(tc, test_libipm_receive_s_type); + tcase_add_test(tc, test_libipm_receive_B_type); + tcase_add_test(tc, test_libipm_receive_no_type); + tcase_add_test(tc, test_libipm_receive_unexpected_type); + tcase_add_test(tc, test_libipm_receive_unsupported_type); + tcase_add_test(tc, test_libipm_receive_unimplemented_type); + tcase_add_test(tc, test_libipm_receive_bad_header); + tcase_add_test(tc, test_libipm_receive_msg_erase); + tcase_add_test(tc, test_libipm_receive_programming_errors); + + return s; +} diff --git a/tests/libipm/test_libipm_send_calls.c b/tests/libipm/test_libipm_send_calls.c new file mode 100644 index 00000000..09ceb549 --- /dev/null +++ b/tests/libipm/test_libipm_send_calls.c @@ -0,0 +1,506 @@ + +#if defined(HAVE_CONFIG_H) +#include "config_ac.h" +#endif + +#include +#include + +#include "libipm.h" +#include "os_calls.h" +#include "string_calls.h" +#include "trans.h" + +#include "test_libipm.h" + +/* Random(ish) values for test_libipm_append_all_test */ +#define ALL_TEST_y_VALUE 45 +#define ALL_TEST_b_VALUE 0 +#define ALL_TEST_n_VALUE -14 +#define ALL_TEST_q_VALUE 327 +#define ALL_TEST_i_VALUE -100000 +#define ALL_TEST_u_VALUE 100000 +#define ALL_TEST_x_VALUE -4000000000L +#define ALL_TEST_t_VALUE 8000000000L +#define ALL_TEST_s_VALUE \ + 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0' +#define ALL_TEST_B_VALUE 'a', 'z', 'A', 'Z', '0', '9', '#' + +/* This macro generates two unsigned char values, separated by a comma, + * representing a 2's complement little-endian 16-bit integer */ +#define BITS16_LE(x) ((x) & 0xff), (((unsigned int)(x) >> 8) & 0xff) + +/* This macro generates four unsigned char values, separated by a comma, + * representing a 2's complement little-endian 32-bit integer */ +#define BITS32_LE(x) BITS16_LE((x) & 0xffff), \ + BITS16_LE(((unsigned int)(x) >> 16) & 0xffff) + +/* This macro generates eight unsigned char values, separated by a comma, + * representing a 2's complement little-endian 64-bit integer */ +#define BITS64_LE(x) BITS32_LE((uint64_t)(x) & 0xffffffff), \ + BITS32_LE(((uint64_t)(x) >> 32) & 0xffffffff) + +/* Expected buffer contents for test_libipm_append_all_test */ +static const unsigned char all_test_expected_buff[] = +{ + BITS16_LE(LIBIPM_VERSION), + BITS16_LE(73), /* Header : message length */ + BITS16_LE(LIBIPM_FAC_TEST), + BITS16_LE(TEST_MESSAGE_NO), + BITS32_LE(0), + /* ------------------- */ + 'y', ALL_TEST_y_VALUE, + 'b', ALL_TEST_b_VALUE, + 'n', BITS16_LE(ALL_TEST_n_VALUE), + 'q', BITS16_LE(ALL_TEST_q_VALUE), + 'i', BITS32_LE(ALL_TEST_i_VALUE), + 'u', BITS32_LE(ALL_TEST_u_VALUE), + 'x', BITS64_LE(ALL_TEST_x_VALUE), + 't', BITS64_LE(ALL_TEST_t_VALUE), + /* String + terminator */ + 's', ALL_TEST_s_VALUE, + /* Fixed size block */ + 'B', BITS16_LE(7) /* length */, ALL_TEST_B_VALUE +}; + +/* Type used to map small test values (i.e. simple types that fit in an int) + * to expected status codes if we try to output them for a given tyoe */ +struct int_test_value +{ + int value; + enum libipm_status expected_status; +}; + +/***************************************************************************//** + * Compares the data in a stream against an expected data block + * + * @param s Stream to check + * @param expdata Expected data + * @aram explen Expected data len + */ +static void +check_stream_data_eq(const struct stream *s, + const unsigned char *expected_data, + unsigned int expected_len) +{ + check_binary_data_eq((unsigned char *)s->data, + (unsigned int)(s->end - s->data), + expected_data, + expected_len); +} + + +/***************************************************************************//** + * Value checks for small types (i.e. those that fit in an 'int' */ +static void +test_small_type_values(char typechar, + const struct int_test_value v[], unsigned int count) +{ + unsigned int i; + enum libipm_status status; + char format[] = { typechar, '\0'}; + + for (i = 0 ; i < count; ++i) + { + status = libipm_msg_out_init(g_t_out, TEST_MESSAGE_NO, + format, v[i].value); + if (status != v[i].expected_status) + { + ck_abort_msg("Test value %d. Expected status %d, got %d", + v[i].value, v[i].expected_status, status); + } + } +} + +/***************************************************************************//** + * Checks we can add a simple type right at the end of a buffer, but that + * a buffer overflow is also detected if there's not enough space. + */ +static void +test_append_at_end_of_message(char typechar, unsigned int wire_size) +{ + char padding[LIBIPM_MAX_PAYLOAD_SIZE] = {0}; + struct libipm_fsb desc; + enum libipm_status status; + const char format[] = {'B', typechar, '\0'}; + + /* Construct a descriptor for a binary object that fills most of + * the output buffer, leaving just enough space for our type at the end. + * Three bytes are needed for the binary descriptor overhead */ + desc.data = padding; + desc.datalen = LIBIPM_MAX_PAYLOAD_SIZE - 3 - wire_size; + + /* Check we're OK at the end of the buffer... */ + status = libipm_msg_out_init(g_t_out, TEST_MESSAGE_NO, format, &desc, 0); + ck_assert_int_eq(status, E_LI_SUCCESS); + + /* ..but if the value would overflow the end of the packet it's an error */ + ++desc.datalen; + status = libipm_msg_out_init(g_t_out, TEST_MESSAGE_NO, format, &desc, 0); + ck_assert_int_eq(status, E_LI_BUFFER_OVERFLOW); +} + +/***************************************************************************//** + * Appends all data types to a transport, and checks the binary data in + * the output stream is as expected */ +START_TEST(test_libipm_append_all_test) +{ + ck_assert_ptr_ne(g_t_out, NULL); + + static char string[] = { ALL_TEST_s_VALUE }; + static char binary[] = { ALL_TEST_B_VALUE }; + struct libipm_fsb binary_desc = { (void *)binary, sizeof(binary) }; + enum libipm_status status; + + status = libipm_msg_out_init( + g_t_out, TEST_MESSAGE_NO, + "ybnqiuxtsB", + ALL_TEST_y_VALUE, + ALL_TEST_b_VALUE, + ALL_TEST_n_VALUE, + ALL_TEST_q_VALUE, + ALL_TEST_i_VALUE, + ALL_TEST_u_VALUE, + ALL_TEST_x_VALUE, + ALL_TEST_t_VALUE, + string, + &binary_desc); + ck_assert_int_eq(status, E_LI_SUCCESS); + + libipm_msg_out_mark_end(g_t_out); + + check_stream_data_eq( + g_t_out->out_s, + all_test_expected_buff, sizeof(all_test_expected_buff)); +} +END_TEST + +/***************************************************************************//** + * Checks various send errors for 'y' + */ +START_TEST(test_libipm_send_y_type) +{ + static const struct int_test_value test_values[] = + { + {0, E_LI_SUCCESS}, + {255, E_LI_SUCCESS}, + {-1, E_LI_BAD_VALUE}, + {256, E_LI_BAD_VALUE}, + {63336, E_LI_BAD_VALUE}, + {2147483647, E_LI_BAD_VALUE} + }; + + test_small_type_values('y', test_values, + sizeof(test_values) / sizeof(test_values[0])); + + test_append_at_end_of_message('y', 2); +} +END_TEST + +/***************************************************************************//** + * Checks various send errors for 'b' + */ +START_TEST(test_libipm_send_b_type) +{ + static const struct int_test_value test_values[] = + { + {0, E_LI_SUCCESS}, + {1, E_LI_SUCCESS}, + {2, E_LI_BAD_VALUE}, + {-1, E_LI_BAD_VALUE}, + {256, E_LI_BAD_VALUE} + }; + + test_small_type_values('b', test_values, + sizeof(test_values) / sizeof(test_values[0])); + + test_append_at_end_of_message('b', 2); +} +END_TEST + +/***************************************************************************//** + * Checks various send errors for 'n' + */ +START_TEST(test_libipm_send_n_type) +{ + static const struct int_test_value test_values[] = + { + {-32768, E_LI_SUCCESS}, + {32767, E_LI_SUCCESS}, + {-32769, E_LI_BAD_VALUE}, + {32768, E_LI_BAD_VALUE}, + {2147483647, E_LI_BAD_VALUE} + }; + + test_small_type_values('n', test_values, + sizeof(test_values) / sizeof(test_values[0])); + + test_append_at_end_of_message('n', 3); +} +END_TEST + +/***************************************************************************//** + * Checks various send errors for 'q' + */ +START_TEST(test_libipm_send_q_type) +{ + static const struct int_test_value test_values[] = + { + {0, E_LI_SUCCESS}, + {65535, E_LI_SUCCESS}, + {-1, E_LI_BAD_VALUE}, + {65536, E_LI_BAD_VALUE}, + {2147483647, E_LI_BAD_VALUE} + }; + + test_small_type_values('q', test_values, + sizeof(test_values) / sizeof(test_values[0])); + + test_append_at_end_of_message('q', 3); +} +END_TEST + +/***************************************************************************//** + * Checks various send errors for 'i' + */ +START_TEST(test_libipm_send_i_type) +{ +#if SIZEOF_INT > 4 + static const struct int_test_value test_values[] = + { + {-2147483648, E_LI_SUCCESS}, + {2147483647, E_LI_SUCCESS}, + {-2147483649, E_LI_BAD_VALUE}, + {2147483648, E_LI_BAD_VALUE}, + }; + + test_small_type_values('i', test_values, + sizeof(test_values) / sizeof(test_values[0])); +#endif + test_append_at_end_of_message('i', 5); +} +END_TEST + +/***************************************************************************//** + * Checks various send errors for 'u' + */ +START_TEST(test_libipm_send_u_type) +{ +#if SIZEOF_INT > 4 + static const struct int_test_value test_values[] = + { + {0, E_LI_SUCCESS}, + {4294967296, E_LI_SUCCESS}, + {-1, E_LI_BAD_VALUE}, + {4294967297, E_LI_BAD_VALUE}, + }; + + test_small_type_values('u', test_values, + sizeof(test_values) / sizeof(test_values[0])); +#endif + test_append_at_end_of_message('u', 5); +} +END_TEST + +/***************************************************************************//** + * Checks various send errors for 'x' + */ +START_TEST(test_libipm_send_x_type) +{ + test_append_at_end_of_message('x', 9); +} +END_TEST + +/***************************************************************************//** + * Checks various send errors for 't' + */ +START_TEST(test_libipm_send_t_type) +{ + test_append_at_end_of_message('t', 9); +} +END_TEST + +/***************************************************************************//** + * Checks various send errors for 's' + */ +START_TEST(test_libipm_send_s_type) +{ + enum libipm_status status; + + /* The maximum string length would be LIBIPM_MAX_PAYLOAD_SIZE-2, as we + * need one char for the 's' type marker, and another for the string + * terminator */ + + /* Construct a string one character too long to fit in the buffer */ + char str[LIBIPM_MAX_PAYLOAD_SIZE]; + + g_memset(str, 'A', sizeof(str)); + str[sizeof(str) - 1] = '\0'; + + /* A write should overflow... */ + status = libipm_msg_out_init(g_t_out, TEST_MESSAGE_NO, "s", str); + ck_assert_int_eq(status, E_LI_BUFFER_OVERFLOW); + + /* .. but a string one character shorter should be OK */ + str[sizeof(str) - 2] = '\0'; + status = libipm_msg_out_init(g_t_out, TEST_MESSAGE_NO, "s", str); + ck_assert_int_eq(status, E_LI_SUCCESS); + + /* Check passing a NULL string doesn't crash the program */ + status = libipm_msg_out_init(g_t_out, TEST_MESSAGE_NO, "s", NULL); + ck_assert_int_eq(status, E_LI_PROGRAM_ERROR); +} +END_TEST + +/***************************************************************************//** + * Checks various send errors for 'B' + */ +START_TEST(test_libipm_send_B_type) +{ + enum libipm_status status; + char bin[LIBIPM_MAX_PAYLOAD_SIZE] = {0}; + struct libipm_fsb desc; + + /* The maximum binary block length would be LIBIPM_MAX_PAYLOAD_SIZE - 3, + * as we need one char for the 'B' type marker, and two for the length + * + * Construct a descriptor for a binary object that completely fills + * the output buffer */ + desc.data = bin; + desc.datalen = LIBIPM_MAX_PAYLOAD_SIZE - 3; + + /* Check it fits in the buffer... */ + status = libipm_msg_out_init(g_t_out, TEST_MESSAGE_NO, "B", &desc); + ck_assert_int_eq(status, E_LI_SUCCESS); + + /* ..but one byte bigger, and it won't fit */ + ++desc.datalen; + status = libipm_msg_out_init(g_t_out, TEST_MESSAGE_NO, "B", &desc); + ck_assert_int_eq(status, E_LI_BUFFER_OVERFLOW); + --desc.datalen; + + /* Check NULL pointers don't crash the library */ + desc.data = NULL; + status = libipm_msg_out_init(g_t_out, TEST_MESSAGE_NO, "B", &desc); + ck_assert_int_eq(status, E_LI_PROGRAM_ERROR); + status = libipm_msg_out_init(g_t_out, TEST_MESSAGE_NO, "B", NULL); + ck_assert_int_eq(status, E_LI_PROGRAM_ERROR); +} +END_TEST + +/***************************************************************************//** + * Checks send errors for unsupported and unimplemented types + */ +START_TEST(test_libipm_send_bad_types) +{ + enum libipm_status status; + enum libipm_status expected_status; + char format[2] = {0}; + char c; + + for (c = 0x1; c < 0x7f; ++c) + { + if (!isprint(c)) + { + continue; + } + + if (g_strchr(g_supported_types, c) == NULL) + { + expected_status = E_LI_UNSUPPORTED_TYPE; + } + else if (g_strchr(g_unimplemented_types, c) != NULL) + { + expected_status = E_LI_UNIMPLEMENTED_TYPE; + } + else + { + continue; + } + + format[0] = c; + status = libipm_msg_out_init(g_t_out, + TEST_MESSAGE_NO_STRING_NO, + format, NULL); + if (status != expected_status) + { + ck_abort_msg("Output char '%c'. Expected status %d, got %d", + c, expected_status, status); + } + } +} +END_TEST + +/***************************************************************************//** + * Checks message erase works as expected + * + * Also calls libipm_msg_out_append() which isn't exercised anywhere else + */ +START_TEST(test_libipm_send_msg_erase) +{ + enum libipm_status status; + const char *username = "username"; + const char *password = "password"; + + status = libipm_msg_out_init(g_t_out, TEST_MESSAGE_NO, NULL); + ck_assert_int_eq(status, E_LI_SUCCESS); + + status = libipm_msg_out_append(g_t_out, "ss", username, password); + ck_assert_int_eq(status, E_LI_SUCCESS); + + ck_assert_int_ne(does_stream_contain_string(g_t_out->out_s, password), 0); + libipm_msg_out_erase(g_t_out); + ck_assert_int_eq(does_stream_contain_string(g_t_out->out_s, password), 0); +} +END_TEST + +/***************************************************************************//** + * Exercises codepaths that shouldn't be called (programming errors) + */ +START_TEST(test_libipm_send_programming_errors) +{ + enum libipm_status status; + + status = libipm_msg_out_init(g_t_vanilla, TEST_MESSAGE_NO, NULL); + ck_assert_int_eq(status, E_LI_PROGRAM_ERROR); + + status = libipm_msg_out_append(g_t_vanilla, "i", 0); + ck_assert_int_eq(status, E_LI_PROGRAM_ERROR); + + libipm_msg_out_mark_end(g_t_vanilla); /* No status to check */ + + status = libipm_msg_out_simple_send(g_t_vanilla, TEST_MESSAGE_NO, "i", 0); + ck_assert_int_eq(status, E_LI_PROGRAM_ERROR); + +} +END_TEST + +/******************************************************************************/ +Suite * +make_suite_test_libipm_send_calls(void) +{ + Suite *s; + TCase *tc; + + s = suite_create("libipm_send"); + + tc = tcase_create("libipm_send"); + suite_add_tcase(s, tc); + tcase_add_test(tc, test_libipm_append_all_test); + tcase_add_test(tc, test_libipm_send_y_type); + tcase_add_test(tc, test_libipm_send_b_type); + tcase_add_test(tc, test_libipm_send_n_type); + tcase_add_test(tc, test_libipm_send_q_type); + tcase_add_test(tc, test_libipm_send_i_type); + tcase_add_test(tc, test_libipm_send_u_type); + tcase_add_test(tc, test_libipm_send_x_type); + tcase_add_test(tc, test_libipm_send_t_type); + tcase_add_test(tc, test_libipm_send_s_type); + tcase_add_test(tc, test_libipm_send_B_type); + tcase_add_test(tc, test_libipm_send_y_type); + tcase_add_test(tc, test_libipm_send_bad_types); + tcase_add_test(tc, test_libipm_send_msg_erase); + tcase_add_test(tc, test_libipm_send_programming_errors); + + return s; +}