diff --git a/include/qb/qblist.h b/include/qb/qblist.h index da1b760..9d4da53 100644 --- a/include/qb/qblist.h +++ b/include/qb/qblist.h @@ -1,315 +1,315 @@ /* * Copyright (C) 2010-2020 Red Hat, Inc. * * Author: Steven Dake * * This file is part of libqb. * * libqb is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libqb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libqb. If not, see . */ #ifndef QB_LIST_H_DEFINED #define QB_LIST_H_DEFINED /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ #include #include #include /** * @file qblist.h * This is a kernel style list implementation. * * @author Steven Dake */ struct qb_list_head { struct qb_list_head *next; struct qb_list_head *prev; }; /** * @def QB_LIST_DECLARE() * Declare and initialize a list head. */ #define QB_LIST_DECLARE(name) \ struct qb_list_head name = { &(name), &(name) } #define QB_INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) /** * Initialize the list entry. * * Points next and prev pointers to head. * @param head pointer to the list head */ static inline void qb_list_init(struct qb_list_head *head) { head->next = head; head->prev = head; } /** * Add this element to the list. * * @param element the new element to insert. * @param head pointer to the list head */ static inline void qb_list_add(struct qb_list_head *element, struct qb_list_head *head) { head->next->prev = element; element->next = head->next; element->prev = head; head->next = element; } /** * Add to the list (but at the end of the list). * * @param element pointer to the element to add * @param head pointer to the list head * @see qb_list_add() */ static inline void qb_list_add_tail(struct qb_list_head *element, struct qb_list_head *head) { head->prev->next = element; element->next = head; element->prev = head->prev; head->prev = element; } /** * Delete an entry from the list. * * @param _remove the list item to remove */ static inline void qb_list_del(struct qb_list_head *_remove) { _remove->next->prev = _remove->prev; _remove->prev->next = _remove->next; } /** * Replace old entry by new one * @param old_one: the element to be replaced * @param new_one: the new element to insert */ static inline void qb_list_replace(struct qb_list_head *old_one, struct qb_list_head *new_one) { new_one->next = old_one->next; new_one->next->prev = new_one; new_one->prev = old_one->prev; new_one->prev->next = new_one; } /** * Tests whether list is the last entry in list head * @param list: the entry to test * @param head: the head of the list * @return boolean true/false */ static inline int qb_list_is_last(const struct qb_list_head *list, const struct qb_list_head *head) { return list->next == head; } /** * A quick test to see if the list is empty (pointing to it's self). * @param head pointer to the list head * @return boolean true/false */ static inline int32_t qb_list_empty(const struct qb_list_head *head) { return head->next == head; } /** * Join two lists. * @param list the new list to add. * @param head the place to add it in the first list. * * @note The "list" is reinitialised */ static inline void qb_list_splice(struct qb_list_head *list, struct qb_list_head *head) { struct qb_list_head *first = list->next; struct qb_list_head *last = list->prev; struct qb_list_head *at = head->next; if (!qb_list_empty(list)) { first->prev = head; head->next = first; last->next = at; at->prev = last; } } /** * Join two lists, each list being a queue * @param list: the new list to add. * @param head: the place to add it in the first list. */ static inline void qb_list_splice_tail(struct qb_list_head *list, struct qb_list_head *head) { struct qb_list_head *first = list->next; struct qb_list_head *last = list->prev; struct qb_list_head *at = head; if (!qb_list_empty(list)) { first->prev = head->prev; head->prev->next = first; last->next = at; at->prev = last; } } /** * Get the struct for this entry * @param ptr: the &struct list_head pointer. * @param type: the type of the struct this is embedded in. * @param member: the name of the list_struct within the struct. */ #define qb_list_entry(ptr,type,member) ({ \ - ((type *)((char*)ptr - offsetof(type, member))); }) + ((type *)(void*)((char*)ptr - offsetof(type, member))); }) /** * Get the first element from a list * @param ptr: the &struct list_head pointer. * @param type: the type of the struct this is embedded in. * @param member: the name of the list_struct within the struct. */ #define qb_list_first_entry(ptr, type, member) \ qb_list_entry((ptr)->next, type, member) /** * Iterate over a list * @param pos: the &struct list_head to use as a loop counter. * @param head: the head for your list. */ #define qb_list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /** * Iterate over a list backwards * @param pos: the &struct list_head to use as a loop counter. * @param head: the head for your list. */ #define qb_list_for_each_reverse(pos, head) \ for (pos = (head)->prev; pos != (head); pos = pos->prev) /** * Iterate over a list safe against removal of list entry * @param pos: the &struct list_head to use as a loop counter. * @param n: another &struct list_head to use as temporary storage * @param head: the head for your list. */ #define qb_list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) /** * Iterate over list of given type * @param pos: the type * to use as a loop counter. * @param head: the head for your list. * @param member: the name of the list_struct within the struct. */ #define qb_list_for_each_entry(pos, head, member) \ for (pos = qb_list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = qb_list_entry(pos->member.next, typeof(*pos), member)) /** * Iterate backwards over list of given type. * @param pos: the type to use as a loop counter. * @param head: the head for your list. * @param member: the name of the list_struct within the struct. */ #define qb_list_for_each_entry_reverse(pos, head, member) \ for (pos = qb_list_entry((head)->prev, typeof(*pos), member); \ &pos->member != (head); \ pos = qb_list_entry(pos->member.prev, typeof(*pos), member)) /** * Iterate over list of given type safe against removal of list entry * @param pos: the type * to use as a loop cursor. * @param n: another type * to use as temporary storage * @param head: the head for your list. * @param member: the name of the list_struct within the struct. */ #define qb_list_for_each_entry_safe(pos, n, head, member) \ for (pos = qb_list_entry((head)->next, typeof(*pos), member), \ n = qb_list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = qb_list_entry(n->member.next, typeof(*n), member)) /** * Iterate backwards over list safe against removal * @param pos: the type * to use as a loop cursor. * @param n: another type * to use as temporary storage * @param head: the head for your list. * @param member: the name of the list_struct within the struct. */ #define qb_list_for_each_entry_safe_reverse(pos, n, head, member) \ for (pos = qb_list_entry((head)->prev, typeof(*pos), member), \ n = qb_list_entry(pos->member.prev, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = qb_list_entry(n->member.prev, typeof(*n), member)) /** * Iterate over list of given type from the current point * @param pos: the type * to use as a loop cursor. * @param head: the head for your list. * @param member: the name of the list_struct within the struct. */ #define qb_list_for_each_entry_from(pos, head, member) \ for (; &pos->member != (head); \ pos = qb_list_entry(pos->member.next, typeof(*pos), member)) /** * Count the number of items in the list. * @param head: the head for your list. * @return length of the list. */ static inline int32_t qb_list_length(struct qb_list_head *head) { struct qb_list_head *item; int32_t length = 0; qb_list_for_each(item, head) length++; return length; } /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* __cplusplus */ /* *INDENT-ON* */ #endif /* QB_LIST_H_DEFINED */ diff --git a/tests/check_array.c b/tests/check_array.c index 1abfd73..eb04aac 100644 --- a/tests/check_array.c +++ b/tests/check_array.c @@ -1,169 +1,169 @@ /* * Copyright (c) 2010 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * This file is part of libqb. * * libqb is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libqb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libqb. If not, see . */ #include "os_base.h" #include "check_common.h" #include #include #include struct test_my_st { int32_t a; int32_t b; int32_t c; int32_t d; }; START_TEST(test_array_limits) { qb_array_t *a; int32_t res; struct test_my_st *st; a = qb_array_create(INT_MAX, sizeof(struct test_my_st)); ck_assert(a == NULL); a = qb_array_create(-56, sizeof(struct test_my_st)); ck_assert(a == NULL); a = qb_array_create(67, 0); ck_assert(a == NULL); /* working array */ a = qb_array_create(10, sizeof(struct test_my_st)); ck_assert(a != NULL); /* out-of-bounds */ res = qb_array_index(a, 10, (void**)&st); ck_assert_int_eq(res, -ERANGE); res = qb_array_index(a, -10, (void**)&st); ck_assert_int_eq(res, -ERANGE); res = qb_array_index(NULL, 1, (void**)&st); ck_assert_int_eq(res, -EINVAL); res = qb_array_index(a, -10, NULL); ck_assert_int_eq(res, -EINVAL); qb_array_free(a); } END_TEST START_TEST(test_array_alloc_free) { qb_array_t *a; a = qb_array_create(65536, sizeof(struct test_my_st)); qb_array_free(a); } END_TEST START_TEST(test_array_correct_retrieval) { qb_array_t *a; int32_t i; int32_t res; struct test_my_st *st; a = qb_array_create(112, sizeof(struct test_my_st)); for (i = 0; i < 112; i++) { res = qb_array_index(a, i, (void**)&st); ck_assert_int_eq(res, 0); st->a = i; st->b = i+1; st->c = i+2; st->d = i+3; } /* read back */ for (i = 0; i < 112; i++) { res = qb_array_index(a, i, (void**)&st); ck_assert_int_eq(res, 0); ck_assert_int_eq(st->a, i); ck_assert_int_eq(st->b, i+1); ck_assert_int_eq(st->c, i+2); ck_assert_int_eq(st->d, i+3); } qb_array_free(a); } END_TEST START_TEST(test_array_static_memory) { qb_array_t *a; int32_t res; struct test_my_st *st_old; struct test_my_st *st; a = qb_array_create(112, sizeof(struct test_my_st)); res = qb_array_index(a, 99, (void**)&st_old); ck_assert_int_eq(res, 0); res = qb_array_grow(a, 1453); ck_assert_int_eq(res, 0); res = qb_array_index(a, 345, (void**)&st); ck_assert_int_eq(res, 0); st->a = 411; /* confirm the pointer is the same after a grow */ res = qb_array_index(a, 99, (void**)&st); ck_assert_int_eq(res, 0); ck_assert(st == st_old); qb_array_free(a); } END_TEST static Suite *array_suite(void) { TCase *tc; Suite *s = suite_create("qb_array"); - add_tcase(s, tc, test_array_limits); - add_tcase(s, tc, test_array_alloc_free); - add_tcase(s, tc, test_array_correct_retrieval); - add_tcase(s, tc, test_array_static_memory); + add_tcase(s, tc, test_array_limits, 0); + add_tcase(s, tc, test_array_alloc_free, 0); + add_tcase(s, tc, test_array_correct_retrieval, 0); + add_tcase(s, tc, test_array_static_memory, 0); return s; } int32_t main(void) { int32_t number_failed; Suite *s = array_suite(); SRunner *sr = srunner_create(s); qb_log_init("check", LOG_USER, LOG_EMERG); atexit(qb_log_fini); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_INFO); qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/check_common.h b/tests/check_common.h index 5f1f536..84f1130 100644 --- a/tests/check_common.h +++ b/tests/check_common.h @@ -1,69 +1,43 @@ /* * Copyright (c) 2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Pokorny * * This file is part of libqb. * * libqb is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libqb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libqb. If not, see . */ #ifndef QB_CHECK_COMMON_H_DEFINED #define QB_CHECK_COMMON_H_DEFINED #include -/* - Auxiliary macros - */ +#define _STRINGIFY(arg) #arg +#define STRINGIFY(arg) _STRINGIFY(arg) -#define JOIN(a, b) a##b -#define _STRINGIFY(arg) #arg -#define STRINGIFY(arg) _STRINGIFY(arg) - -/* wide-spread technique, see, e.g., - http://cplusplus.co.il/2010/07/17/variadic-macro-to-count-number-of-arguments - */ -#define VA_ARGS_CNT(...) VA_ARGS_CNT_IMPL(__VA_ARGS__,8,7,6,5,4,3,2,1,_) -#define VA_ARGS_CNT_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N - -/* add_tcase "overloading" per argument count; - "func" argument is assumed to always starts with "test_", which is skipped - for the purpose of naming the respective test case that's being created */ - -#define add_tcase_select(cnt) JOIN(add_tcase_, cnt) -#define add_tcase_3(suite, tcase, func) \ - do { \ - (tcase) = tcase_create(STRINGIFY(func) + sizeof("test")); \ - tcase_add_test((tcase), func); \ - suite_add_tcase((suite), (tcase)); \ - } while (0) -#define add_tcase_4(suite, tcase, func, timeout) \ +#define add_tcase(suite, tcase, func, timeout) \ do { \ - (tcase) = tcase_create(STRINGIFY(func) + sizeof("test")); \ + const char * fname = STRINGIFY(func); \ + (tcase) = tcase_create(fname + sizeof("test") ); \ tcase_add_test((tcase), func); \ - tcase_set_timeout((tcase), (timeout)); \ + if (timeout != 0) { \ + tcase_set_timeout((tcase), (timeout)); \ + } \ suite_add_tcase((suite), (tcase)); \ } while (0) -/* - Use-me macros - */ - -/* add_tcase(, , [, ]) */ -#define add_tcase(...) add_tcase_select(VA_ARGS_CNT(__VA_ARGS__))(__VA_ARGS__) - #endif diff --git a/tests/check_ipc.c b/tests/check_ipc.c index d2d59e6..e4a6e5a 100644 --- a/tests/check_ipc.c +++ b/tests/check_ipc.c @@ -1,2457 +1,2457 @@ /* * Copyright (c) 2010 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * This file is part of libqb. * * libqb is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libqb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libqb. If not, see . */ #include "os_base.h" #include #include #include #include #include #ifdef HAVE_GLIB #include #endif #include "check_common.h" #include #include #include #include #include #ifdef HAVE_FAILURE_INJECTION #include "_failure_injection.h" #endif #define NUM_STRESS_CONNECTIONS 5000 static char ipc_name[256]; #define DEFAULT_MAX_MSG_SIZE (8192*16) #ifndef __clang__ static int CALCULATED_DGRAM_MAX_MSG_SIZE = 0; #define DGRAM_MAX_MSG_SIZE \ (CALCULATED_DGRAM_MAX_MSG_SIZE == 0 ? \ CALCULATED_DGRAM_MAX_MSG_SIZE = qb_ipcc_verify_dgram_max_msg_size(DEFAULT_MAX_MSG_SIZE) : \ CALCULATED_DGRAM_MAX_MSG_SIZE) #define MAX_MSG_SIZE (ipc_type == QB_IPC_SOCKET ? DGRAM_MAX_MSG_SIZE : DEFAULT_MAX_MSG_SIZE) #else /* because of clang's 'variable length array in structure' extension will never be supported; assign default for SHM as we'll skip test that would use run-time established value (via qb_ipcc_verify_dgram_max_msg_size), anyway */ -static const int MAX_MSG_SIZE = DEFAULT_MAX_MSG_SIZE; +#define MAX_MSG_SIZE DEFAULT_MAX_MSG_SIZE #endif /* The size the giant msg's data field needs to be to make * this the largests msg we can successfully send. */ -#define GIANT_MSG_DATA_SIZE MAX_MSG_SIZE - sizeof(struct qb_ipc_response_header) - 8 +#define GIANT_MSG_DATA_SIZE (MAX_MSG_SIZE - sizeof(struct qb_ipc_response_header) - 8) static int enforce_server_buffer; static qb_ipcc_connection_t *conn; static enum qb_ipc_type ipc_type; static enum qb_loop_priority global_loop_prio = QB_LOOP_MED; static bool global_use_glib; static int global_pipefd[2]; enum my_msg_ids { IPC_MSG_REQ_TX_RX, IPC_MSG_RES_TX_RX, IPC_MSG_REQ_DISPATCH, IPC_MSG_RES_DISPATCH, IPC_MSG_REQ_BULK_EVENTS, IPC_MSG_RES_BULK_EVENTS, IPC_MSG_REQ_STRESS_EVENT, IPC_MSG_RES_STRESS_EVENT, IPC_MSG_REQ_SELF_FEED, IPC_MSG_RES_SELF_FEED, IPC_MSG_REQ_SERVER_FAIL, IPC_MSG_RES_SERVER_FAIL, IPC_MSG_REQ_SERVER_DISCONNECT, IPC_MSG_RES_SERVER_DISCONNECT, }; /* these 2 functions from pacemaker code */ static enum qb_ipcs_rate_limit conv_libqb_prio2ratelimit(enum qb_loop_priority prio) { /* this is an inversion of what libqb's qb_ipcs_request_rate_limit does */ enum qb_ipcs_rate_limit ret = QB_IPCS_RATE_NORMAL; switch (prio) { case QB_LOOP_LOW: ret = QB_IPCS_RATE_SLOW; break; case QB_LOOP_HIGH: ret = QB_IPCS_RATE_FAST; break; default: qb_log(LOG_DEBUG, "Invalid libqb's loop priority %d," " assuming QB_LOOP_MED", prio); /* fall-through */ case QB_LOOP_MED: break; } return ret; } #ifdef HAVE_GLIB static gint conv_prio_libqb2glib(enum qb_loop_priority prio) { gint ret = G_PRIORITY_DEFAULT; switch (prio) { case QB_LOOP_LOW: ret = G_PRIORITY_LOW; break; case QB_LOOP_HIGH: ret = G_PRIORITY_HIGH; break; default: qb_log(LOG_DEBUG, "Invalid libqb's loop priority %d," " assuming QB_LOOP_MED", prio); /* fall-through */ case QB_LOOP_MED: break; } return ret; } /* these 3 glue functions inspired from pacemaker, too */ static gboolean gio_source_prepare(GSource *source, gint *timeout) { qb_enter(); *timeout = 500; return FALSE; } static gboolean gio_source_check(GSource *source) { qb_enter(); return TRUE; } static gboolean gio_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { gboolean ret = G_SOURCE_CONTINUE; qb_enter(); if (callback) { ret = callback(user_data); } return ret; } static GSourceFuncs gio_source_funcs = { .prepare = gio_source_prepare, .check = gio_source_check, .dispatch = gio_source_dispatch, }; #endif /* Test Cases * * 1) basic send & recv different message sizes * * 2) send message to start dispatch (confirm receipt) * * 3) flow control * * 4) authentication * * 5) thread safety * * 6) cleanup * * 7) service availability * * 8) multiple services * * 9) setting perms on the sockets */ static qb_loop_t *my_loop; static qb_ipcs_service_t* s1; static int32_t turn_on_fc = QB_FALSE; static int32_t fc_enabled = 89; static int32_t send_event_on_created = QB_FALSE; static int32_t disconnect_after_created = QB_FALSE; static int32_t num_bulk_events = 10; static int32_t num_stress_events = 30000; static int32_t reference_count_test = QB_FALSE; static int32_t multiple_connections = QB_FALSE; static int32_t set_perms_on_socket = QB_FALSE; static int32_t exit_handler(int32_t rsignal, void *data) { qb_log(LOG_DEBUG, "caught signal %d", rsignal); qb_ipcs_destroy(s1); exit(0); } static void set_ipc_name(const char *prefix) { FILE *f; char process_name[256]; /* The process-unique part of the IPC name has already been decided * and stored in the file IPC_TEST_NAME_FILE */ f = fopen(IPC_TEST_NAME_FILE, "r"); if (f) { fgets(process_name, sizeof(process_name), f); /* Remove any trailing LF that might be lurking */ if (process_name[strlen(process_name)-1] == '\n') { process_name[strlen(process_name)-1] = '\0'; } fclose(f); snprintf(ipc_name, sizeof(ipc_name), "%.44s%s", prefix, process_name); } else { /* This is the old code, use only as a fallback */ static char t_sec[3] = ""; if (t_sec[0] == '\0') { const char *const found = strrchr(__TIME__, ':'); strncpy(t_sec, found ? found + 1 : "-", sizeof(t_sec) - 1); t_sec[sizeof(t_sec) - 1] = '\0'; } snprintf(ipc_name, sizeof(ipc_name), "%.44s%s%lX%.4x", prefix, t_sec, (unsigned long)getpid(), (unsigned) ((long) time(NULL) % (0x10000))); } } static int pipe_writer(int fd, int revents, void *data) { qb_enter(); static const char buf[8] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; ssize_t wbytes = 0, wbytes_sum = 0; //for (size_t i = 0; i < SIZE_MAX; i++) { for (size_t i = 0; i < 4096; i++) { wbytes_sum += wbytes; if ((wbytes = write(fd, buf, sizeof(buf))) == -1) { if (errno != EAGAIN) { perror("write"); exit(-1); } break; } } if (wbytes_sum > 0) { qb_log(LOG_DEBUG, "written %zd bytes", wbytes_sum); } qb_leave(); return 1; } static int pipe_reader(int fd, int revents, void *data) { qb_enter(); ssize_t rbytes, rbytes_sum = 0; size_t cnt = SIZE_MAX; char buf[4096] = { '\0' }; while ((rbytes = read(fd, buf, sizeof(buf))) > 0 && rbytes < cnt) { cnt -= rbytes; rbytes_sum += rbytes; } if (rbytes_sum > 0) { ck_assert(buf[0] != '\0'); /* avoid dead store elimination */ qb_log(LOG_DEBUG, "read %zd bytes", rbytes_sum); sleep(1); } qb_leave(); return 1; } #if HAVE_GLIB static gboolean gio_pipe_reader(void *data) { return (pipe_reader(*((int *) data), 0, NULL) > 0); } static gboolean gio_pipe_writer(void *data) { return (pipe_writer(*((int *) data), 0, NULL) > 0); } #endif static int32_t s1_msg_process_fn(qb_ipcs_connection_t *c, void *data, size_t size) { struct qb_ipc_request_header *req_pt = (struct qb_ipc_request_header *)data; struct qb_ipc_response_header response = { 0, }; ssize_t res; if (req_pt->id == IPC_MSG_REQ_TX_RX) { response.size = sizeof(struct qb_ipc_response_header); response.id = IPC_MSG_RES_TX_RX; response.error = 0; res = qb_ipcs_response_send(c, &response, response.size); if (res < 0) { qb_perror(LOG_INFO, "qb_ipcs_response_send"); } else if (res != response.size) { qb_log(LOG_DEBUG, "qb_ipcs_response_send %zd != %d", res, response.size); } if (turn_on_fc) { qb_ipcs_request_rate_limit(s1, QB_IPCS_RATE_OFF); } } else if (req_pt->id == IPC_MSG_REQ_DISPATCH) { response.size = sizeof(struct qb_ipc_response_header); response.id = IPC_MSG_RES_DISPATCH; response.error = 0; res = qb_ipcs_event_send(c, &response, sizeof(response)); if (res < 0) { qb_perror(LOG_INFO, "qb_ipcs_event_send"); } } else if (req_pt->id == IPC_MSG_REQ_BULK_EVENTS) { int32_t m; int32_t num; struct qb_ipcs_connection_stats_2 *stats; uint32_t max_size = MAX_MSG_SIZE; response.size = sizeof(struct qb_ipc_response_header); response.error = 0; stats = qb_ipcs_connection_stats_get_2(c, QB_FALSE); num = stats->event_q_length; free(stats); /* crazy large message */ res = qb_ipcs_event_send(c, &response, max_size*10); ck_assert_int_eq(res, -EMSGSIZE); /* send one event before responding */ res = qb_ipcs_event_send(c, &response, sizeof(response)); ck_assert_int_eq(res, sizeof(response)); response.id++; /* There should be one more item in the event queue now. */ stats = qb_ipcs_connection_stats_get_2(c, QB_FALSE); ck_assert_int_eq(stats->event_q_length - num, 1); free(stats); /* send response */ response.id = IPC_MSG_RES_BULK_EVENTS; res = qb_ipcs_response_send(c, &response, response.size); ck_assert_int_eq(res, sizeof(response)); /* send the rest of the events after the response */ for (m = 1; m < num_bulk_events; m++) { res = qb_ipcs_event_send(c, &response, sizeof(response)); if (res == -EAGAIN || res == -ENOBUFS) { /* retry */ usleep(1000); m--; continue; } ck_assert_int_eq(res, sizeof(response)); response.id++; } } else if (req_pt->id == IPC_MSG_REQ_STRESS_EVENT) { struct { struct qb_ipc_response_header hdr __attribute__ ((aligned(8))); char data[GIANT_MSG_DATA_SIZE] __attribute__ ((aligned(8))); uint32_t sent_msgs __attribute__ ((aligned(8))); } __attribute__ ((aligned(8))) giant_event_send; int32_t m; response.size = sizeof(struct qb_ipc_response_header); response.error = 0; response.id = IPC_MSG_RES_STRESS_EVENT; res = qb_ipcs_response_send(c, &response, response.size); ck_assert_int_eq(res, sizeof(response)); giant_event_send.hdr.error = 0; giant_event_send.hdr.id = IPC_MSG_RES_STRESS_EVENT; for (m = 0; m < num_stress_events; m++) { size_t sent_len = sizeof(struct qb_ipc_response_header); if (((m+1) % 1000) == 0) { sent_len = sizeof(giant_event_send); giant_event_send.sent_msgs = m + 1; } giant_event_send.hdr.size = sent_len; res = qb_ipcs_event_send(c, &giant_event_send, sent_len); if (res < 0) { if (res == -EAGAIN || res == -ENOBUFS) { /* yield to the receive process */ usleep(1000); m--; continue; } else { qb_perror(LOG_DEBUG, "sending stress events"); ck_assert_int_eq(res, sent_len); } } else if (((m+1) % 1000) == 0) { qb_log(LOG_DEBUG, "SENT: %d stress events sent", m+1); } giant_event_send.hdr.id++; } } else if (req_pt->id == IPC_MSG_REQ_SELF_FEED) { if (pipe(global_pipefd) != 0) { perror("pipefd"); ck_assert(0); } fcntl(global_pipefd[0], F_SETFL, O_NONBLOCK); fcntl(global_pipefd[1], F_SETFL, O_NONBLOCK); if (global_use_glib) { #ifdef HAVE_GLIB GSource *source_r, *source_w; source_r = g_source_new(&gio_source_funcs, sizeof(GSource)); source_w = g_source_new(&gio_source_funcs, sizeof(GSource)); ck_assert(source_r != NULL && source_w != NULL); g_source_set_priority(source_r, conv_prio_libqb2glib(QB_LOOP_HIGH)); g_source_set_priority(source_w, conv_prio_libqb2glib(QB_LOOP_HIGH)); g_source_set_can_recurse(source_r, FALSE); g_source_set_can_recurse(source_w, FALSE); g_source_set_callback(source_r, gio_pipe_reader, &global_pipefd[0], NULL); g_source_set_callback(source_w, gio_pipe_writer, &global_pipefd[1], NULL); g_source_add_unix_fd(source_r, global_pipefd[0], G_IO_IN); g_source_add_unix_fd(source_w, global_pipefd[1], G_IO_OUT); g_source_attach(source_r, NULL); g_source_attach(source_w, NULL); #else ck_assert(0); #endif } else { qb_loop_poll_add(my_loop, QB_LOOP_HIGH, global_pipefd[1], POLLOUT|POLLERR, NULL, pipe_writer); qb_loop_poll_add(my_loop, QB_LOOP_HIGH, global_pipefd[0], POLLIN|POLLERR, NULL, pipe_reader); } } else if (req_pt->id == IPC_MSG_REQ_SERVER_FAIL) { exit(0); } else if (req_pt->id == IPC_MSG_REQ_SERVER_DISCONNECT) { multiple_connections = QB_FALSE; qb_ipcs_disconnect(c); } return 0; } static int32_t my_job_add(enum qb_loop_priority p, void *data, qb_loop_job_dispatch_fn fn) { return qb_loop_job_add(my_loop, p, data, fn); } static int32_t my_dispatch_add(enum qb_loop_priority p, int32_t fd, int32_t events, void *data, qb_ipcs_dispatch_fn_t fn) { return qb_loop_poll_add(my_loop, p, fd, events, data, fn); } static int32_t my_dispatch_mod(enum qb_loop_priority p, int32_t fd, int32_t events, void *data, qb_ipcs_dispatch_fn_t fn) { return qb_loop_poll_mod(my_loop, p, fd, events, data, fn); } static int32_t my_dispatch_del(int32_t fd) { return qb_loop_poll_del(my_loop, fd); } /* taken from examples/ipcserver.c, with s/my_g/gio/ */ #ifdef HAVE_GLIB #include static qb_array_t *gio_map; static GMainLoop *glib_loop; struct gio_to_qb_poll { int32_t is_used; int32_t events; int32_t source; int32_t fd; void *data; qb_ipcs_dispatch_fn_t fn; enum qb_loop_priority p; }; static gboolean gio_read_socket(GIOChannel * gio, GIOCondition condition, gpointer data) { struct gio_to_qb_poll *adaptor = (struct gio_to_qb_poll *)data; gint fd = g_io_channel_unix_get_fd(gio); qb_enter(); return (adaptor->fn(fd, condition, adaptor->data) == 0); } static void gio_poll_destroy(gpointer data) { struct gio_to_qb_poll *adaptor = (struct gio_to_qb_poll *)data; adaptor->is_used--; if (adaptor->is_used == 0) { qb_log(LOG_DEBUG, "fd %d adaptor destroyed\n", adaptor->fd); adaptor->fd = 0; adaptor->source = 0; } } static int32_t gio_dispatch_update(enum qb_loop_priority p, int32_t fd, int32_t evts, void *data, qb_ipcs_dispatch_fn_t fn, gboolean is_new) { struct gio_to_qb_poll *adaptor; GIOChannel *channel; int32_t res = 0; qb_enter(); res = qb_array_index(gio_map, fd, (void **)&adaptor); if (res < 0) { return res; } if (adaptor->is_used && adaptor->source) { if (is_new) { return -EEXIST; } g_source_remove(adaptor->source); adaptor->source = 0; } channel = g_io_channel_unix_new(fd); if (!channel) { return -ENOMEM; } adaptor->fn = fn; adaptor->events = evts; adaptor->data = data; adaptor->p = p; adaptor->is_used++; adaptor->fd = fd; adaptor->source = g_io_add_watch_full(channel, conv_prio_libqb2glib(p), evts, gio_read_socket, adaptor, gio_poll_destroy); /* we are handing the channel off to be managed by mainloop now. * remove our reference. */ g_io_channel_unref(channel); return 0; } static int32_t gio_dispatch_add(enum qb_loop_priority p, int32_t fd, int32_t evts, void *data, qb_ipcs_dispatch_fn_t fn) { return gio_dispatch_update(p, fd, evts, data, fn, TRUE); } static int32_t gio_dispatch_mod(enum qb_loop_priority p, int32_t fd, int32_t evts, void *data, qb_ipcs_dispatch_fn_t fn) { return gio_dispatch_update(p, fd, evts, data, fn, FALSE); } static int32_t gio_dispatch_del(int32_t fd) { struct gio_to_qb_poll *adaptor; if (qb_array_index(gio_map, fd, (void **)&adaptor) == 0) { g_source_remove(adaptor->source); adaptor->source = 0; } return 0; } #endif /* HAVE_GLIB */ static int32_t s1_connection_closed(qb_ipcs_connection_t *c) { if (multiple_connections) { return 0; } /* Stop the connection being freed when we call qb_ipcs_disconnect() in the callback */ if (disconnect_after_created == QB_TRUE) { disconnect_after_created = QB_FALSE; return 1; } qb_enter(); qb_leave(); return 0; } static void outq_flush (void *data) { static int i = 0; struct cs_ipcs_conn_context *cnx; cnx = qb_ipcs_context_get(data); qb_log(LOG_DEBUG,"iter %u\n", i); i++; if (i == 2) { qb_ipcs_destroy(s1); s1 = NULL; } /* if the reference counting is not working, this should fail * for i > 1. */ qb_ipcs_event_send(data, "test", 4); assert(memcmp(cnx, "test", 4) == 0); if (i < 5) { qb_loop_job_add(my_loop, QB_LOOP_HIGH, data, outq_flush); } else { /* this single unref should clean everything up. */ qb_ipcs_connection_unref(data); qb_log(LOG_INFO, "end of test, stopping loop"); qb_loop_stop(my_loop); } } static void s1_connection_destroyed(qb_ipcs_connection_t *c) { if (multiple_connections) { return; } qb_enter(); if (reference_count_test) { struct cs_ipcs_conn_context *cnx; cnx = qb_ipcs_context_get(c); free(cnx); } else { qb_loop_stop(my_loop); } qb_leave(); } static int32_t s1_connection_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) { if (set_perms_on_socket) { qb_ipcs_connection_auth_set(c, 555, 741, S_IRWXU|S_IRWXG|S_IROTH|S_IWOTH); } return 0; } static void s1_connection_created(qb_ipcs_connection_t *c) { uint32_t max = MAX_MSG_SIZE; if (multiple_connections) { return; } if (send_event_on_created) { struct qb_ipc_response_header response; int32_t res; response.size = sizeof(struct qb_ipc_response_header); response.id = IPC_MSG_RES_DISPATCH; response.error = 0; res = qb_ipcs_event_send(c, &response, sizeof(response)); ck_assert_int_eq(res, response.size); } if (reference_count_test) { struct cs_ipcs_conn_context *context; qb_ipcs_connection_ref(c); qb_loop_job_add(my_loop, QB_LOOP_HIGH, c, outq_flush); context = calloc(1, 20); memcpy(context, "test", 4); qb_ipcs_context_set(c, context); } ck_assert_int_le(max, qb_ipcs_connection_get_buffer_size(c)); } static volatile sig_atomic_t usr1_bit; static void usr1_bit_setter(int signal) { if (signal == SIGUSR1) { usr1_bit = 1; } } #define READY_SIGNALLER(name, data_arg) void (name)(void *data_arg) typedef READY_SIGNALLER(ready_signaller_fn, ); static READY_SIGNALLER(usr1_signaller, parent_target) { kill(*((pid_t *) parent_target), SIGUSR1); } #define NEW_PROCESS_RUNNER(name, ready_signaller_arg, signaller_data_arg, data_arg) \ void (name)(ready_signaller_fn ready_signaller_arg, \ void *signaller_data_arg, void *data_arg) typedef NEW_PROCESS_RUNNER(new_process_runner_fn, , , ); static NEW_PROCESS_RUNNER(run_ipc_server, ready_signaller, signaller_data, data) { int32_t res; qb_loop_signal_handle handle; struct qb_ipcs_service_handlers sh = { .connection_accept = s1_connection_accept, .connection_created = s1_connection_created, .msg_process = s1_msg_process_fn, .connection_destroyed = s1_connection_destroyed, .connection_closed = s1_connection_closed, }; struct qb_ipcs_poll_handlers ph; uint32_t max_size = MAX_MSG_SIZE; my_loop = qb_loop_create(); qb_loop_signal_add(my_loop, QB_LOOP_HIGH, SIGTERM, NULL, exit_handler, &handle); s1 = qb_ipcs_create(ipc_name, 4, ipc_type, &sh); ck_assert(s1 != 0); if (global_loop_prio != QB_LOOP_MED) { qb_ipcs_request_rate_limit(s1, conv_libqb_prio2ratelimit(global_loop_prio)); } if (global_use_glib) { #ifdef HAVE_GLIB ph = (struct qb_ipcs_poll_handlers) { .job_add = NULL, .dispatch_add = gio_dispatch_add, .dispatch_mod = gio_dispatch_mod, .dispatch_del = gio_dispatch_del, }; glib_loop = g_main_loop_new(NULL, FALSE); gio_map = qb_array_create_2(16, sizeof(struct gio_to_qb_poll), 1); ck_assert(gio_map != NULL); #else ck_assert(0); #endif } else { ph = (struct qb_ipcs_poll_handlers) { .job_add = my_job_add, .dispatch_add = my_dispatch_add, .dispatch_mod = my_dispatch_mod, .dispatch_del = my_dispatch_del, }; } if (enforce_server_buffer) { qb_ipcs_enforce_buffer_size(s1, max_size); } qb_ipcs_poll_handlers_set(s1, &ph); res = qb_ipcs_run(s1); ck_assert_int_eq(res, 0); if (ready_signaller != NULL) { ready_signaller(signaller_data); } if (global_use_glib) { #ifdef HAVE_GLIB g_main_loop_run(glib_loop); #endif } else { qb_loop_run(my_loop); } qb_log(LOG_DEBUG, "loop finished - done ..."); } static pid_t run_function_in_new_process(const char *role, new_process_runner_fn new_process_runner, void *data) { char formatbuf[1024]; pid_t parent_target, pid1, pid2; struct sigaction orig_sa, purpose_sa; sigset_t orig_mask, purpose_mask, purpose_clear_mask; sigemptyset(&purpose_mask); sigaddset(&purpose_mask, SIGUSR1); sigprocmask(SIG_BLOCK, &purpose_mask, &orig_mask); purpose_clear_mask = orig_mask; sigdelset(&purpose_clear_mask, SIGUSR1); purpose_sa.sa_handler = usr1_bit_setter; purpose_sa.sa_mask = purpose_mask; purpose_sa.sa_flags = SA_RESTART; /* Double-fork so the servers can be reaped in a timely manner */ parent_target = getpid(); pid1 = fork(); if (pid1 == 0) { pid2 = fork(); if (pid2 == -1) { fprintf (stderr, "Can't fork twice\n"); exit(0); } if (pid2 == 0) { sigprocmask(SIG_SETMASK, &orig_mask, NULL); if (role == NULL) { qb_log_format_set(QB_LOG_STDERR, "lib/%f|%l[%P] %b"); } else { snprintf(formatbuf, sizeof(formatbuf), "lib/%%f|%%l|%s[%%P] %%b", role); qb_log_format_set(QB_LOG_STDERR, formatbuf); } new_process_runner(usr1_signaller, &parent_target, data); exit(0); } else { waitpid(pid2, NULL, 0); exit(0); } } usr1_bit = 0; /* XXX assume never fails */ sigaction(SIGUSR1, &purpose_sa, &orig_sa); do { /* XXX assume never fails with EFAULT */ sigsuspend(&purpose_clear_mask); } while (usr1_bit != 1); usr1_bit = 0; sigprocmask(SIG_SETMASK, &orig_mask, NULL); /* give children a slight/non-strict scheduling advantage */ sched_yield(); return pid1; } static void request_server_exit(void) { struct qb_ipc_request_header req_header; struct qb_ipc_response_header res_header; struct iovec iov[1]; int32_t res; /* * tell the server to exit */ req_header.id = IPC_MSG_REQ_SERVER_FAIL; req_header.size = sizeof(struct qb_ipc_request_header); iov[0].iov_len = req_header.size; iov[0].iov_base = (void*)&req_header; ck_assert_int_eq(QB_TRUE, qb_ipcc_is_connected(conn)); res = qb_ipcc_sendv_recv(conn, iov, 1, &res_header, sizeof(struct qb_ipc_response_header), -1); /* * confirm we get -ENOTCONN or ECONNRESET */ if (res != -ECONNRESET && res != -ENOTCONN) { qb_log(LOG_ERR, "id:%d size:%d", res_header.id, res_header.size); ck_assert_int_eq(res, -ENOTCONN); } } static void kill_server(pid_t pid) { kill(pid, SIGTERM); waitpid(pid, NULL, 0); } static int32_t verify_graceful_stop(pid_t pid) { int wait_rc = 0; int status = 0; int rc = 0; int tries; /* We need the server to be able to exit by itself */ for (tries = 10; tries >= 0; tries--) { sleep(1); wait_rc = waitpid(pid, &status, WNOHANG); if (wait_rc > 0) { break; } } ck_assert_int_eq(wait_rc, pid); rc = WIFEXITED(status); if (rc) { rc = WEXITSTATUS(status); ck_assert_int_eq(rc, 0); } else { ck_assert(rc != 0); } return 0; } struct my_req { struct qb_ipc_request_header hdr; char message[1024 * 1024]; }; static struct my_req request; static int32_t send_and_check(int32_t req_id, uint32_t size, int32_t ms_timeout, int32_t expect_perfection) { struct qb_ipc_response_header res_header; int32_t res; int32_t try_times = 0; uint32_t max_size = MAX_MSG_SIZE; request.hdr.id = req_id; request.hdr.size = sizeof(struct qb_ipc_request_header) + size; /* check that we can't send a message that is too big * and we get the right return code. */ res = qb_ipcc_send(conn, &request, max_size*2); ck_assert_int_eq(res, -EMSGSIZE); repeat_send: res = qb_ipcc_send(conn, &request, request.hdr.size); try_times++; if (res < 0) { if (res == -EAGAIN && try_times < 10) { goto repeat_send; } else { if (res == -EAGAIN && try_times >= 10) { fc_enabled = QB_TRUE; } errno = -res; qb_perror(LOG_INFO, "qb_ipcc_send"); return res; } } if (req_id == IPC_MSG_REQ_DISPATCH) { res = qb_ipcc_event_recv(conn, &res_header, sizeof(struct qb_ipc_response_header), ms_timeout); } else { res = qb_ipcc_recv(conn, &res_header, sizeof(struct qb_ipc_response_header), ms_timeout); } if (res == -EINTR) { return -1; } if (res == -EAGAIN || res == -ETIMEDOUT) { fc_enabled = QB_TRUE; qb_perror(LOG_DEBUG, "qb_ipcc_recv"); return res; } if (expect_perfection) { ck_assert_int_eq(res, sizeof(struct qb_ipc_response_header)); ck_assert_int_eq(res_header.id, req_id + 1); ck_assert_int_eq(res_header.size, sizeof(struct qb_ipc_response_header)); } return res; } static int32_t process_async_connect(int32_t fd, int32_t revents, void *data) { qb_loop_t *cl = (qb_loop_t *)data; int res; res = qb_ipcc_connect_continue(conn); ck_assert_int_eq(res, 0); qb_loop_stop(cl); return 0; } static void test_ipc_connect_async(void) { struct qb_ipc_request_header req_header; struct qb_ipc_response_header res_header; int32_t res; pid_t pid; uint32_t max_size = MAX_MSG_SIZE; int connect_fd; struct iovec iov[1]; static qb_loop_t *cl; pid = run_function_in_new_process("server", run_ipc_server, NULL); ck_assert(pid != -1); conn = qb_ipcc_connect_async(ipc_name, max_size, &connect_fd); ck_assert(conn != NULL); cl = qb_loop_create(); res = qb_loop_poll_add(cl, QB_LOOP_MED, connect_fd, POLLIN, cl, process_async_connect); ck_assert_int_eq(res, 0); qb_loop_run(cl); /* Send some data */ req_header.id = IPC_MSG_REQ_TX_RX; req_header.size = sizeof(struct qb_ipc_request_header); iov[0].iov_len = req_header.size; iov[0].iov_base = (void*)&req_header; res = qb_ipcc_sendv_recv(conn, iov, 1, &res_header, sizeof(struct qb_ipc_response_header), 5000); ck_assert_int_ge(res, 0); request_server_exit(); verify_graceful_stop(pid); qb_ipcc_disconnect(conn); } static void test_ipc_txrx_timeout(void) { struct qb_ipc_request_header req_header; struct qb_ipc_response_header res_header; struct iovec iov[1]; int32_t res; int32_t c = 0; int32_t j = 0; pid_t pid; uint32_t max_size = MAX_MSG_SIZE; pid = run_function_in_new_process("server", run_ipc_server, NULL); ck_assert(pid != -1); do { conn = qb_ipcc_connect(ipc_name, max_size); if (conn == NULL) { j = waitpid(pid, NULL, WNOHANG); ck_assert_int_eq(j, 0); poll(NULL, 0, 400); c++; } } while (conn == NULL && c < 5); ck_assert(conn != NULL); /* The dispatch response will only come over * the event channel, we want to verify the receive times * out when an event is returned with no response */ req_header.id = IPC_MSG_REQ_DISPATCH; req_header.size = sizeof(struct qb_ipc_request_header); iov[0].iov_len = req_header.size; iov[0].iov_base = (void*)&req_header; res = qb_ipcc_sendv_recv(conn, iov, 1, &res_header, sizeof(struct qb_ipc_response_header), 5000); ck_assert_int_eq(res, -ETIMEDOUT); request_server_exit(); verify_graceful_stop(pid); /* * this needs to free up the shared mem */ qb_ipcc_disconnect(conn); } static int32_t recv_timeout = -1; static void test_ipc_txrx(void) { int32_t j; int32_t c = 0; size_t size; pid_t pid; uint32_t max_size = MAX_MSG_SIZE; pid = run_function_in_new_process("server", run_ipc_server, NULL); ck_assert(pid != -1); do { conn = qb_ipcc_connect(ipc_name, max_size); if (conn == NULL) { j = waitpid(pid, NULL, WNOHANG); ck_assert_int_eq(j, 0); poll(NULL, 0, 400); c++; } } while (conn == NULL && c < 5); ck_assert(conn != NULL); size = QB_MIN(sizeof(struct qb_ipc_request_header), 64); for (j = 1; j < 19; j++) { size *= 2; if (size >= max_size) break; if (send_and_check(IPC_MSG_REQ_TX_RX, size, recv_timeout, QB_TRUE) < 0) { break; } } if (turn_on_fc) { /* can't signal server to shutdown if flow control is on */ ck_assert_int_eq(fc_enabled, QB_TRUE); qb_ipcc_disconnect(conn); /* TODO - figure out why this sleep is necessary */ sleep(1); kill_server(pid); } else { request_server_exit(); qb_ipcc_disconnect(conn); verify_graceful_stop(pid); } } static void test_ipc_getauth(void) { int32_t j; int32_t c = 0; pid_t pid; pid_t spid; uid_t suid; gid_t sgid; int res; uint32_t max_size = MAX_MSG_SIZE; pid = run_function_in_new_process("server", run_ipc_server, NULL); ck_assert(pid != -1); do { conn = qb_ipcc_connect(ipc_name, max_size); if (conn == NULL) { j = waitpid(pid, NULL, WNOHANG); ck_assert_int_eq(j, 0); poll(NULL, 0, 400); c++; } } while (conn == NULL && c < 5); ck_assert(conn != NULL); res = qb_ipcc_auth_get(NULL, NULL, NULL, NULL); ck_assert(res == -EINVAL); res = qb_ipcc_auth_get(conn, &spid, &suid, &sgid); ck_assert(res == 0); #ifndef HAVE_GETPEEREID /* GETPEEREID doesn't return a PID */ ck_assert(spid != 0); #endif ck_assert(suid == getuid()); ck_assert(sgid == getgid()); request_server_exit(); qb_ipcc_disconnect(conn); verify_graceful_stop(pid); } static void test_ipc_exit(void) { struct qb_ipc_request_header req_header; struct qb_ipc_response_header res_header; struct iovec iov[1]; int32_t res; int32_t c = 0; int32_t j = 0; pid_t pid; uint32_t max_size = MAX_MSG_SIZE; pid = run_function_in_new_process("server", run_ipc_server, NULL); ck_assert(pid != -1); do { conn = qb_ipcc_connect(ipc_name, max_size); if (conn == NULL) { j = waitpid(pid, NULL, WNOHANG); ck_assert_int_eq(j, 0); poll(NULL, 0, 400); c++; } } while (conn == NULL && c < 5); ck_assert(conn != NULL); req_header.id = IPC_MSG_REQ_TX_RX; req_header.size = sizeof(struct qb_ipc_request_header); iov[0].iov_len = req_header.size; iov[0].iov_base = (void*)&req_header; res = qb_ipcc_sendv_recv(conn, iov, 1, &res_header, sizeof(struct qb_ipc_response_header), -1); ck_assert_int_eq(res, sizeof(struct qb_ipc_response_header)); request_server_exit(); verify_graceful_stop(pid); /* * this needs to free up the shared mem */ qb_ipcc_disconnect(conn); } START_TEST(test_ipc_exit_us) { qb_enter(); ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); recv_timeout = 5000; test_ipc_exit(); qb_leave(); } END_TEST START_TEST(test_ipc_exit_shm) { qb_enter(); ipc_type = QB_IPC_SHM; set_ipc_name(__func__); recv_timeout = 1000; test_ipc_exit(); qb_leave(); } END_TEST START_TEST(test_ipc_txrx_shm_timeout) { qb_enter(); ipc_type = QB_IPC_SHM; set_ipc_name(__func__); test_ipc_txrx_timeout(); qb_leave(); } END_TEST START_TEST(test_ipc_txrx_us_timeout) { qb_enter(); ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); test_ipc_txrx_timeout(); qb_leave(); } END_TEST START_TEST(test_ipc_shm_connect_async) { qb_enter(); ipc_type = QB_IPC_SHM; set_ipc_name(__func__); test_ipc_connect_async(); qb_leave(); } END_TEST START_TEST(test_ipc_us_connect_async) { qb_enter(); ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); test_ipc_connect_async(); qb_leave(); } END_TEST START_TEST(test_ipc_txrx_shm_getauth) { qb_enter(); ipc_type = QB_IPC_SHM; set_ipc_name(__func__); test_ipc_getauth(); qb_leave(); } END_TEST START_TEST(test_ipc_txrx_us_getauth) { qb_enter(); ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); test_ipc_getauth(); qb_leave(); } END_TEST START_TEST(test_ipc_txrx_shm_tmo) { qb_enter(); turn_on_fc = QB_FALSE; ipc_type = QB_IPC_SHM; set_ipc_name(__func__); recv_timeout = 1000; test_ipc_txrx(); qb_leave(); } END_TEST START_TEST(test_ipc_txrx_shm_block) { qb_enter(); turn_on_fc = QB_FALSE; ipc_type = QB_IPC_SHM; set_ipc_name(__func__); recv_timeout = -1; test_ipc_txrx(); qb_leave(); } END_TEST START_TEST(test_ipc_fc_shm) { qb_enter(); turn_on_fc = QB_TRUE; ipc_type = QB_IPC_SHM; recv_timeout = 500; set_ipc_name(__func__); test_ipc_txrx(); qb_leave(); } END_TEST START_TEST(test_ipc_txrx_us_block) { qb_enter(); turn_on_fc = QB_FALSE; ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); recv_timeout = -1; test_ipc_txrx(); qb_leave(); } END_TEST START_TEST(test_ipc_txrx_us_tmo) { qb_enter(); turn_on_fc = QB_FALSE; ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); recv_timeout = 1000; test_ipc_txrx(); qb_leave(); } END_TEST START_TEST(test_ipc_fc_us) { qb_enter(); turn_on_fc = QB_TRUE; ipc_type = QB_IPC_SOCKET; recv_timeout = 500; set_ipc_name(__func__); test_ipc_txrx(); qb_leave(); } END_TEST struct my_res { struct qb_ipc_response_header hdr; char message[1024 * 1024]; }; struct dispatch_data { pid_t server_pid; enum my_msg_ids msg_type; uint32_t repetitions; }; static inline NEW_PROCESS_RUNNER(client_dispatch, ready_signaller, signaller_data, data) { uint32_t max_size = MAX_MSG_SIZE; int32_t size; int32_t c = 0; int32_t j; pid_t server_pid = ((struct dispatch_data *) data)->server_pid; enum my_msg_ids msg_type = ((struct dispatch_data *) data)->msg_type; do { conn = qb_ipcc_connect(ipc_name, max_size); if (conn == NULL) { j = waitpid(server_pid, NULL, WNOHANG); ck_assert_int_eq(j, 0); poll(NULL, 0, 400); c++; } } while (conn == NULL && c < 5); ck_assert(conn != NULL); if (ready_signaller != NULL) { ready_signaller(signaller_data); } size = QB_MIN(sizeof(struct qb_ipc_request_header), 64); for (uint32_t r = ((struct dispatch_data *) data)->repetitions; r > 0; r--) { for (j = 1; j < 19; j++) { size *= 2; if (size >= max_size) break; if (send_and_check(msg_type, size, recv_timeout, QB_TRUE) < 0) { break; } } } } static void test_ipc_dispatch(void) { pid_t pid; struct dispatch_data data; pid = run_function_in_new_process(NULL, run_ipc_server, NULL); ck_assert(pid != -1); data = (struct dispatch_data){.server_pid = pid, .msg_type = IPC_MSG_REQ_DISPATCH, .repetitions = 1}; client_dispatch(NULL, NULL, (void *) &data); request_server_exit(); qb_ipcc_disconnect(conn); verify_graceful_stop(pid); } START_TEST(test_ipc_dispatch_us) { qb_enter(); ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); test_ipc_dispatch(); qb_leave(); } END_TEST static int32_t events_received; static int32_t count_stress_events(int32_t fd, int32_t revents, void *data) { struct { struct qb_ipc_response_header hdr __attribute__ ((aligned(8))); char data[GIANT_MSG_DATA_SIZE] __attribute__ ((aligned(8))); uint32_t sent_msgs __attribute__ ((aligned(8))); } __attribute__ ((aligned(8))) giant_event_recv; qb_loop_t *cl = (qb_loop_t*)data; int32_t res; res = qb_ipcc_event_recv(conn, &giant_event_recv, sizeof(giant_event_recv), -1); if (res > 0) { events_received++; if ((events_received % 1000) == 0) { qb_log(LOG_DEBUG, "RECV: %d stress events processed", events_received); if (res != sizeof(giant_event_recv)) { qb_log(LOG_DEBUG, "Unexpected recv size, expected %d got %d", sizeof(giant_event_recv), res); ck_assert_int_eq(res, sizeof(giant_event_recv)); } else if (giant_event_recv.sent_msgs != events_received) { qb_log(LOG_DEBUG, "Server event mismatch. Server thinks we got %d msgs, but we only received %d", giant_event_recv.sent_msgs, events_received); /* This indicates that data corruption is occurring. Since the events * received is placed at the end of the giant msg, it is possible * that buffers were not allocated correctly resulting in us * reading/writing to uninitialized memeory at some point. */ ck_assert_int_eq(giant_event_recv.sent_msgs, events_received); } } } else if (res != -EAGAIN) { qb_perror(LOG_DEBUG, "count_stress_events"); qb_loop_stop(cl); return -1; } if (events_received >= num_stress_events) { qb_loop_stop(cl); return -1; } return 0; } static int32_t count_bulk_events(int32_t fd, int32_t revents, void *data) { qb_loop_t *cl = (qb_loop_t*)data; struct qb_ipc_response_header res_header; int32_t res; res = qb_ipcc_event_recv(conn, &res_header, sizeof(struct qb_ipc_response_header), -1); if (res > 0) { events_received++; } if (events_received >= num_bulk_events) { qb_loop_stop(cl); return -1; } return 0; } static void test_ipc_stress_connections(void) { int32_t c = 0; int32_t j = 0; uint32_t max_size = MAX_MSG_SIZE; int32_t connections = 0; pid_t pid; multiple_connections = QB_TRUE; qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_CLEAR_ALL, QB_LOG_FILTER_FILE, "*", LOG_TRACE); qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_INFO); qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE); pid = run_function_in_new_process("server", run_ipc_server, NULL); ck_assert(pid != -1); for (connections = 1; connections < NUM_STRESS_CONNECTIONS; connections++) { if (conn) { qb_ipcc_disconnect(conn); conn = NULL; } do { conn = qb_ipcc_connect(ipc_name, max_size); if (conn == NULL) { j = waitpid(pid, NULL, WNOHANG); ck_assert_int_eq(j, 0); sleep(1); c++; } } while (conn == NULL && c < 5); ck_assert(conn != NULL); if (((connections+1) % 1000) == 0) { qb_log(LOG_INFO, "%d ipc connections made", connections+1); } } multiple_connections = QB_FALSE; /* Re-enable logging here so we get the "Free'ing" message which allows for resources.test to clear up after us if needed */ qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_CLEAR_ALL, QB_LOG_FILTER_FILE, "*", LOG_TRACE); qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_TRACE); qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE); request_server_exit(); qb_ipcc_disconnect(conn); verify_graceful_stop(pid); } static void test_ipc_bulk_events(void) { int32_t c = 0; int32_t j = 0; pid_t pid; int32_t res; qb_loop_t *cl; int32_t fd; uint32_t max_size = MAX_MSG_SIZE; pid = run_function_in_new_process("server", run_ipc_server, NULL); ck_assert(pid != -1); do { conn = qb_ipcc_connect(ipc_name, max_size); if (conn == NULL) { j = waitpid(pid, NULL, WNOHANG); ck_assert_int_eq(j, 0); poll(NULL, 0, 400); c++; } } while (conn == NULL && c < 5); ck_assert(conn != NULL); events_received = 0; cl = qb_loop_create(); res = qb_ipcc_fd_get(conn, &fd); ck_assert_int_eq(res, 0); res = qb_loop_poll_add(cl, QB_LOOP_MED, fd, POLLIN, cl, count_bulk_events); ck_assert_int_eq(res, 0); res = send_and_check(IPC_MSG_REQ_BULK_EVENTS, 0, recv_timeout, QB_TRUE); ck_assert_int_eq(res, sizeof(struct qb_ipc_response_header)); qb_loop_run(cl); ck_assert_int_eq(events_received, num_bulk_events); request_server_exit(); qb_ipcc_disconnect(conn); verify_graceful_stop(pid); } static void test_ipc_stress_test(void) { struct { struct qb_ipc_request_header hdr __attribute__ ((aligned(8))); char data[GIANT_MSG_DATA_SIZE] __attribute__ ((aligned(8))); uint32_t sent_msgs __attribute__ ((aligned(8))); } __attribute__ ((aligned(8))) giant_req; struct qb_ipc_response_header res_header; struct iovec iov[1]; int32_t c = 0; int32_t j = 0; pid_t pid; int32_t res; qb_loop_t *cl; int32_t fd; uint32_t max_size = MAX_MSG_SIZE; /* This looks strange, but it serves an important purpose. * This test forces the server to enforce the MAX_MSG_SIZE * limit from the server side, which overrides the client's * buffer limit. To verify this functionality is working * we set the client limit lower than what the server * is enforcing. */ int32_t client_buf_size = max_size - 1024; int32_t real_buf_size; enforce_server_buffer = 1; pid = run_function_in_new_process("server", run_ipc_server, NULL); enforce_server_buffer = 0; ck_assert(pid != -1); do { conn = qb_ipcc_connect(ipc_name, client_buf_size); if (conn == NULL) { j = waitpid(pid, NULL, WNOHANG); ck_assert_int_eq(j, 0); poll(NULL, 0, 400); c++; } } while (conn == NULL && c < 5); ck_assert(conn != NULL); real_buf_size = qb_ipcc_get_buffer_size(conn); ck_assert_int_ge(real_buf_size, max_size); qb_log(LOG_DEBUG, "Testing %d iterations of EVENT msg passing.", num_stress_events); events_received = 0; cl = qb_loop_create(); res = qb_ipcc_fd_get(conn, &fd); ck_assert_int_eq(res, 0); res = qb_loop_poll_add(cl, QB_LOOP_MED, fd, POLLIN, cl, count_stress_events); ck_assert_int_eq(res, 0); res = send_and_check(IPC_MSG_REQ_STRESS_EVENT, 0, recv_timeout, QB_TRUE); qb_loop_run(cl); ck_assert_int_eq(events_received, num_stress_events); giant_req.hdr.id = IPC_MSG_REQ_SERVER_FAIL; giant_req.hdr.size = sizeof(giant_req); if (giant_req.hdr.size <= client_buf_size) { ck_assert_int_eq(1, 0); } iov[0].iov_len = giant_req.hdr.size; iov[0].iov_base = (void*)&giant_req; res = qb_ipcc_sendv_recv(conn, iov, 1, &res_header, sizeof(struct qb_ipc_response_header), -1); if (res != -ECONNRESET && res != -ENOTCONN) { qb_log(LOG_ERR, "id:%d size:%d", res_header.id, res_header.size); ck_assert_int_eq(res, -ENOTCONN); } qb_ipcc_disconnect(conn); verify_graceful_stop(pid); } #ifndef __clang__ /* see variable length array in structure' at the top */ START_TEST(test_ipc_stress_test_us) { qb_enter(); send_event_on_created = QB_FALSE; ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); test_ipc_stress_test(); qb_leave(); } END_TEST #endif START_TEST(test_ipc_stress_connections_us) { qb_enter(); ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); test_ipc_stress_connections(); qb_leave(); } END_TEST START_TEST(test_ipc_bulk_events_us) { qb_enter(); ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); test_ipc_bulk_events(); qb_leave(); } END_TEST static READY_SIGNALLER(connected_signaller, _) { request_server_exit(); } START_TEST(test_ipc_us_native_prio_dlock) { pid_t server_pid, alphaclient_pid; struct dispatch_data data; qb_enter(); ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); /* this is to demonstrate that native event loop can deal even with "extreme" priority disproportions */ global_loop_prio = QB_LOOP_LOW; multiple_connections = QB_TRUE; recv_timeout = -1; server_pid = run_function_in_new_process("server", run_ipc_server, NULL); ck_assert(server_pid != -1); data = (struct dispatch_data){.server_pid = server_pid, .msg_type = IPC_MSG_REQ_SELF_FEED, .repetitions = 1}; alphaclient_pid = run_function_in_new_process("alphaclient", client_dispatch, (void *) &data); ck_assert(alphaclient_pid != -1); //sleep(1); sched_yield(); data.repetitions = 0; client_dispatch(connected_signaller, NULL, (void *) &data); verify_graceful_stop(server_pid); multiple_connections = QB_FALSE; qb_leave(); } END_TEST #if HAVE_GLIB START_TEST(test_ipc_us_glib_prio_dlock) { pid_t server_pid, alphaclient_pid; struct dispatch_data data; qb_enter(); ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); global_use_glib = QB_TRUE; /* this is to make the test pass at all, since GLib is strict on priorities -- QB_LOOP_MED or lower would fail for sure */ global_loop_prio = QB_LOOP_HIGH; multiple_connections = QB_TRUE; recv_timeout = -1; server_pid = run_function_in_new_process("server", run_ipc_server, NULL); ck_assert(server_pid != -1); data = (struct dispatch_data){.server_pid = server_pid, .msg_type = IPC_MSG_REQ_SELF_FEED, .repetitions = 1}; alphaclient_pid = run_function_in_new_process("alphaclient", client_dispatch, (void *) &data); ck_assert(alphaclient_pid != -1); //sleep(1); sched_yield(); data.repetitions = 0; client_dispatch(connected_signaller, NULL, (void *) &data); verify_graceful_stop(server_pid); multiple_connections = QB_FALSE; global_loop_prio = QB_LOOP_MED; global_use_glib = QB_FALSE; qb_leave(); } END_TEST #endif static void test_ipc_event_on_created(void) { int32_t c = 0; int32_t j = 0; pid_t pid; int32_t res; qb_loop_t *cl; int32_t fd; uint32_t max_size = MAX_MSG_SIZE; num_bulk_events = 1; pid = run_function_in_new_process("server", run_ipc_server, NULL); ck_assert(pid != -1); do { conn = qb_ipcc_connect(ipc_name, max_size); if (conn == NULL) { j = waitpid(pid, NULL, WNOHANG); ck_assert_int_eq(j, 0); poll(NULL, 0, 400); c++; } } while (conn == NULL && c < 5); ck_assert(conn != NULL); events_received = 0; cl = qb_loop_create(); res = qb_ipcc_fd_get(conn, &fd); ck_assert_int_eq(res, 0); res = qb_loop_poll_add(cl, QB_LOOP_MED, fd, POLLIN, cl, count_bulk_events); ck_assert_int_eq(res, 0); qb_loop_run(cl); ck_assert_int_eq(events_received, num_bulk_events); request_server_exit(); qb_ipcc_disconnect(conn); verify_graceful_stop(pid); } START_TEST(test_ipc_event_on_created_us) { qb_enter(); send_event_on_created = QB_TRUE; ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); test_ipc_event_on_created(); qb_leave(); } END_TEST static void test_ipc_disconnect_after_created(void) { struct qb_ipc_request_header req_header; struct qb_ipc_response_header res_header; struct iovec iov[1]; int32_t c = 0; int32_t j = 0; pid_t pid; int32_t res; uint32_t max_size = MAX_MSG_SIZE; pid = run_function_in_new_process("server", run_ipc_server, NULL); ck_assert(pid != -1); do { conn = qb_ipcc_connect(ipc_name, max_size); if (conn == NULL) { j = waitpid(pid, NULL, WNOHANG); ck_assert_int_eq(j, 0); poll(NULL, 0, 400); c++; } } while (conn == NULL && c < 5); ck_assert(conn != NULL); ck_assert_int_eq(QB_TRUE, qb_ipcc_is_connected(conn)); req_header.id = IPC_MSG_REQ_SERVER_DISCONNECT; req_header.size = sizeof(struct qb_ipc_request_header); iov[0].iov_len = req_header.size; iov[0].iov_base = (void*)&req_header; res = qb_ipcc_sendv_recv(conn, iov, 1, &res_header, sizeof(struct qb_ipc_response_header), -1); /* * confirm we get -ENOTCONN or -ECONNRESET */ if (res != -ECONNRESET && res != -ENOTCONN) { qb_log(LOG_ERR, "id:%d size:%d", res_header.id, res_header.size); ck_assert_int_eq(res, -ENOTCONN); } ck_assert_int_eq(QB_FALSE, qb_ipcc_is_connected(conn)); qb_ipcc_disconnect(conn); sleep(1); /* Give it time to stop */ kill_server(pid); } START_TEST(test_ipc_disconnect_after_created_us) { qb_enter(); disconnect_after_created = QB_TRUE; ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); test_ipc_disconnect_after_created(); qb_leave(); } END_TEST static void test_ipc_server_fail(void) { int32_t j; int32_t c = 0; pid_t pid; uint32_t max_size = MAX_MSG_SIZE; pid = run_function_in_new_process("server", run_ipc_server, NULL); ck_assert(pid != -1); do { conn = qb_ipcc_connect(ipc_name, max_size); if (conn == NULL) { j = waitpid(pid, NULL, WNOHANG); ck_assert_int_eq(j, 0); poll(NULL, 0, 400); c++; } } while (conn == NULL && c < 5); ck_assert(conn != NULL); request_server_exit(); if (_fi_unlink_inject_failure == QB_TRUE) { _fi_truncate_called = _fi_openat_called = 0; } ck_assert_int_eq(QB_FALSE, qb_ipcc_is_connected(conn)); qb_ipcc_disconnect(conn); if (_fi_unlink_inject_failure == QB_TRUE) { ck_assert_int_ne(_fi_truncate_called + _fi_openat_called, 0); } verify_graceful_stop(pid); } START_TEST(test_ipc_server_fail_soc) { qb_enter(); ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); test_ipc_server_fail(); qb_leave(); } END_TEST START_TEST(test_ipc_dispatch_shm) { qb_enter(); ipc_type = QB_IPC_SHM; set_ipc_name(__func__); test_ipc_dispatch(); qb_leave(); } END_TEST START_TEST(test_ipc_stress_test_shm) { qb_enter(); send_event_on_created = QB_FALSE; ipc_type = QB_IPC_SHM; set_ipc_name(__func__); test_ipc_stress_test(); qb_leave(); } END_TEST START_TEST(test_ipc_stress_connections_shm) { qb_enter(); ipc_type = QB_IPC_SHM; set_ipc_name(__func__); test_ipc_stress_connections(); qb_leave(); } END_TEST // Check perms uses illegal access to libqb internals // DO NOT try this at home. #include "../lib/ipc_int.h" #include "../lib/ringbuffer_int.h" START_TEST(test_ipc_server_perms) { pid_t pid; struct stat st; int j; uint32_t max_size; int res; int c = 0; // Can only test this if we are root if (getuid() != 0) { return; } ipc_type = QB_IPC_SHM; set_perms_on_socket = QB_TRUE; max_size = MAX_MSG_SIZE; pid = run_function_in_new_process("server", run_ipc_server, NULL); ck_assert(pid != -1); do { conn = qb_ipcc_connect(ipc_name, max_size); if (conn == NULL) { j = waitpid(pid, NULL, WNOHANG); ck_assert_int_eq(j, 0); poll(NULL, 0, 400); c++; } } while (conn == NULL && c < 5); ck_assert(conn != NULL); /* Check perms - uses illegal access to libqb internals */ /* BSD uses /var/run for sockets so we can't alter the perms on the directory */ #ifdef __linux__ char sockdir[PATH_MAX]; strcpy(sockdir, conn->request.u.shm.rb->shared_hdr->hdr_path); *strrchr(sockdir, '/') = 0; res = stat(sockdir, &st); ck_assert_int_eq(res, 0); ck_assert(st.st_mode & S_IRWXG); ck_assert_int_eq(st.st_uid, 555); ck_assert_int_eq(st.st_gid, 741); #endif res = stat(conn->request.u.shm.rb->shared_hdr->hdr_path, &st); ck_assert_int_eq(res, 0); ck_assert_int_eq(st.st_uid, 555); ck_assert_int_eq(st.st_gid, 741); qb_ipcc_disconnect(conn); verify_graceful_stop(pid); } END_TEST START_TEST(test_ipc_disp_shm_native_prio_dlock) { pid_t server_pid, alphaclient_pid; struct dispatch_data data; qb_enter(); ipc_type = QB_IPC_SHM; set_ipc_name(__func__); /* this is to demonstrate that native event loop can deal even with "extreme" priority disproportions */ global_loop_prio = QB_LOOP_LOW; multiple_connections = QB_TRUE; recv_timeout = -1; server_pid = run_function_in_new_process("server", run_ipc_server, NULL); ck_assert(server_pid != -1); data = (struct dispatch_data){.server_pid = server_pid, .msg_type = IPC_MSG_REQ_SELF_FEED, .repetitions = 1}; alphaclient_pid = run_function_in_new_process("alphaclient", client_dispatch, (void *) &data); ck_assert(alphaclient_pid != -1); //sleep(1); sched_yield(); data.repetitions = 0; client_dispatch(connected_signaller, NULL, (void *) &data); verify_graceful_stop(server_pid); multiple_connections = QB_FALSE; qb_leave(); } END_TEST #if HAVE_GLIB START_TEST(test_ipc_disp_shm_glib_prio_dlock) { pid_t server_pid, alphaclient_pid; struct dispatch_data data; qb_enter(); ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); global_use_glib = QB_TRUE; /* this is to make the test pass at all, since GLib is strict on priorities -- QB_LOOP_MED or lower would fail for sure */ global_loop_prio = QB_LOOP_HIGH; multiple_connections = QB_TRUE; recv_timeout = -1; server_pid = run_function_in_new_process("server", run_ipc_server, NULL); ck_assert(server_pid != -1); data = (struct dispatch_data){.server_pid = server_pid, .msg_type = IPC_MSG_REQ_SELF_FEED, .repetitions = 1}; alphaclient_pid = run_function_in_new_process("alphaclient", client_dispatch, (void *) &data); ck_assert(alphaclient_pid != -1); //sleep(1); sched_yield(); data.repetitions = 0; client_dispatch(connected_signaller, NULL, (void *) &data); verify_graceful_stop(server_pid); multiple_connections = QB_FALSE; global_loop_prio = QB_LOOP_MED; global_use_glib = QB_FALSE; qb_leave(); } END_TEST #endif START_TEST(test_ipc_bulk_events_shm) { qb_enter(); ipc_type = QB_IPC_SHM; set_ipc_name(__func__); test_ipc_bulk_events(); qb_leave(); } END_TEST START_TEST(test_ipc_event_on_created_shm) { qb_enter(); send_event_on_created = QB_TRUE; ipc_type = QB_IPC_SHM; set_ipc_name(__func__); test_ipc_event_on_created(); qb_leave(); } END_TEST START_TEST(test_ipc_server_fail_shm) { qb_enter(); ipc_type = QB_IPC_SHM; set_ipc_name(__func__); test_ipc_server_fail(); qb_leave(); } END_TEST #ifdef HAVE_FAILURE_INJECTION START_TEST(test_ipcc_truncate_when_unlink_fails_shm) { char sock_file[PATH_MAX]; struct sockaddr_un socka; qb_enter(); ipc_type = QB_IPC_SHM; set_ipc_name(__func__); sprintf(sock_file, "%s/%s", SOCKETDIR, ipc_name); sock_file[sizeof(socka.sun_path)] = '\0'; /* If there's an old socket left from a previous run this test will fail unexpectedly, so try to remove it first */ unlink(sock_file); _fi_unlink_inject_failure = QB_TRUE; test_ipc_server_fail(); _fi_unlink_inject_failure = QB_FALSE; unlink(sock_file); qb_leave(); } END_TEST #endif static void test_ipc_service_ref_count(void) { int32_t c = 0; int32_t j = 0; pid_t pid; uint32_t max_size = MAX_MSG_SIZE; reference_count_test = QB_TRUE; pid = run_function_in_new_process("server", run_ipc_server, NULL); ck_assert(pid != -1); do { conn = qb_ipcc_connect(ipc_name, max_size); if (conn == NULL) { j = waitpid(pid, NULL, WNOHANG); ck_assert_int_eq(j, 0); (void)poll(NULL, 0, 400); c++; } } while (conn == NULL && c < 5); ck_assert(conn != NULL); sleep(5); kill_server(pid); } START_TEST(test_ipc_service_ref_count_shm) { qb_enter(); ipc_type = QB_IPC_SHM; set_ipc_name(__func__); test_ipc_service_ref_count(); qb_leave(); } END_TEST START_TEST(test_ipc_service_ref_count_us) { qb_enter(); ipc_type = QB_IPC_SOCKET; set_ipc_name(__func__); test_ipc_service_ref_count(); qb_leave(); } END_TEST #if 0 static void test_max_dgram_size(void) { /* most implementations will not let you set a dgram buffer * of 1 million bytes. This test verifies that the we can detect * the max dgram buffersize regardless, and that the value we detect * is consistent. */ int32_t init; int32_t i; qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_REMOVE, QB_LOG_FILTER_FILE, "*", LOG_TRACE); init = qb_ipcc_verify_dgram_max_msg_size(1000000); ck_assert(init > 0); for (i = 0; i < 100; i++) { int try = qb_ipcc_verify_dgram_max_msg_size(1000000); #if 0 ck_assert_int_eq(init, try); #else /* extra troubleshooting, report also on i and errno variables; related: https://github.com/ClusterLabs/libqb/issues/234 */ if (init != try) { #ifdef ci_dump_shm_usage system("df -h | grep -e /shm >/tmp/_shm_usage"); #endif ck_abort_msg("Assertion 'init==try' failed:" " init==%#x, try==%#x, i=%d, errno=%d", init, try, i, errno); } #endif } qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_TRACE); } START_TEST(test_ipc_max_dgram_size) { qb_enter(); test_max_dgram_size(); qb_leave(); } END_TEST #endif static Suite * make_shm_suite(void) { TCase *tc; Suite *s = suite_create("shm"); add_tcase(s, tc, test_ipc_shm_connect_async, 7); add_tcase(s, tc, test_ipc_txrx_shm_getauth, 7); add_tcase(s, tc, test_ipc_txrx_shm_timeout, 28); add_tcase(s, tc, test_ipc_server_fail_shm, 7); add_tcase(s, tc, test_ipc_txrx_shm_block, 7); add_tcase(s, tc, test_ipc_txrx_shm_tmo, 7); add_tcase(s, tc, test_ipc_fc_shm, 7); add_tcase(s, tc, test_ipc_dispatch_shm, 15); add_tcase(s, tc, test_ipc_stress_test_shm, 15); add_tcase(s, tc, test_ipc_bulk_events_shm, 15); add_tcase(s, tc, test_ipc_exit_shm, 6); add_tcase(s, tc, test_ipc_event_on_created_shm, 9); add_tcase(s, tc, test_ipc_service_ref_count_shm, 9); add_tcase(s, tc, test_ipc_server_perms, 7); add_tcase(s, tc, test_ipc_stress_connections_shm, 3600 /* ? */); add_tcase(s, tc, test_ipc_disp_shm_native_prio_dlock, 15); #if HAVE_GLIB add_tcase(s, tc, test_ipc_disp_shm_glib_prio_dlock, 15); #endif #ifdef HAVE_FAILURE_INJECTION add_tcase(s, tc, test_ipcc_truncate_when_unlink_fails_shm, 8); #endif return s; } static Suite * make_soc_suite(void) { Suite *s = suite_create("socket"); TCase *tc; add_tcase(s, tc, test_ipc_us_connect_async, 7); add_tcase(s, tc, test_ipc_txrx_us_getauth, 7); add_tcase(s, tc, test_ipc_txrx_us_timeout, 28); /* Commented out for the moment as space in /dev/shm on the CI machines causes random failures */ /* add_tcase(s, tc, test_ipc_max_dgram_size, 30); */ add_tcase(s, tc, test_ipc_server_fail_soc, 7); add_tcase(s, tc, test_ipc_txrx_us_block, 7); add_tcase(s, tc, test_ipc_txrx_us_tmo, 7); add_tcase(s, tc, test_ipc_fc_us, 7); add_tcase(s, tc, test_ipc_exit_us, 6); add_tcase(s, tc, test_ipc_dispatch_us, 15); #ifndef __clang__ /* see variable length array in structure' at the top */ add_tcase(s, tc, test_ipc_stress_test_us, 58); #endif add_tcase(s, tc, test_ipc_bulk_events_us, 15); add_tcase(s, tc, test_ipc_event_on_created_us, 9); add_tcase(s, tc, test_ipc_disconnect_after_created_us, 9); add_tcase(s, tc, test_ipc_service_ref_count_us, 9); add_tcase(s, tc, test_ipc_stress_connections_us, 3600 /* ? */); add_tcase(s, tc, test_ipc_us_native_prio_dlock, 15); #if HAVE_GLIB add_tcase(s, tc, test_ipc_us_glib_prio_dlock, 15); #endif return s; } int32_t main(void) { int32_t number_failed; SRunner *sr; Suite *s; int32_t do_shm_tests = QB_TRUE; set_ipc_name("ipc_test"); #ifdef DISABLE_IPC_SHM do_shm_tests = QB_FALSE; #endif /* DISABLE_IPC_SHM */ s = make_soc_suite(); sr = srunner_create(s); if (do_shm_tests) { srunner_add_suite(sr, make_shm_suite()); } qb_log_init("check", LOG_USER, LOG_EMERG); atexit(qb_log_fini); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_TRACE); qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE); qb_log_format_set(QB_LOG_STDERR, "lib/%f|%l| %b"); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/check_list.c b/tests/check_list.c index b827668..051d67d 100644 --- a/tests/check_list.c +++ b/tests/check_list.c @@ -1,107 +1,107 @@ /* * Copyright (c) 2018 Red Hat, Inc. * * All rights reserved. * * Author: Jan Pokorny * * This file is part of libqb. * * libqb is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libqb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libqb. If not, see . */ #include "os_base.h" #include "check_common.h" #include #include typedef struct { struct qb_list_head list; size_t i; } enlistable_num_t; #define DIMOF(_a) sizeof(_a)/sizeof(*(_a)) START_TEST(test_list_iter) { QB_LIST_DECLARE(mylist); enlistable_num_t reference_head[] = { {.i=0}, {.i=1}, {.i=2}, {.i=3} }; enlistable_num_t reference_tail[] = { {.i=4}, {.i=5}, {.i=6}, {.i=7} }; enlistable_num_t *iter, replacement = {.i=8}; size_t iter_i; for (iter_i = DIMOF(reference_head); iter_i > 0; iter_i--) { /* prepends in reverse order */ qb_list_add(&reference_head[iter_i-1].list, &mylist); } for (iter_i = 0; iter_i < DIMOF(reference_tail); iter_i++) { /* appends in natural order */ qb_list_add_tail(&reference_tail[iter_i].list, &mylist); } /* assert the constructed list corresponds to ordered sequence... */ /* ... increasing when iterating forward */ iter_i = 0; qb_list_for_each_entry(iter, &mylist, list) { ck_assert_int_eq(iter->i, iter_i); iter_i++; } /* ... and decreasing when iterating backward */ qb_list_for_each_entry_reverse(iter, &mylist, list) { ck_assert_int_gt(iter_i, 0); ck_assert_int_eq(iter->i, iter_i-1); iter_i--; } ck_assert_int_eq(iter_i, 0); /* also check qb_list_replace and qb_list_first_entry */ qb_list_replace(mylist.next, &replacement.list); ck_assert_int_eq(qb_list_first_entry(&mylist, enlistable_num_t, list)->i, replacement.i); } END_TEST static Suite *array_suite(void) { TCase *tc; Suite *s = suite_create("qb_list"); - add_tcase(s, tc, test_list_iter); + add_tcase(s, tc, test_list_iter, 0); return s; } int32_t main(void) { int32_t number_failed; Suite *s = array_suite(); SRunner *sr = srunner_create(s); qb_log_init("check", LOG_USER, LOG_EMERG); atexit(qb_log_fini); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_INFO); qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/check_log.c b/tests/check_log.c index e5abf40..80f95f1 100644 --- a/tests/check_log.c +++ b/tests/check_log.c @@ -1,1138 +1,1138 @@ /* * Copyright (c) 2011-2015 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * This file is part of libqb. * * libqb is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libqb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libqb. If not, see . */ #include "os_base.h" #include #include "check_common.h" #include #include #include #ifdef USE_JOURNAL #include #endif #include "_syslog_override.h" extern size_t qb_vsnprintf_serialize(char *serialize, size_t max_len, const char *fmt, va_list ap); extern size_t qb_vsnprintf_deserialize(char *string, size_t strlen, const char *buf); static void format_this(char *out, const char *fmt, ...) { char buf[QB_LOG_MAX_LEN]; va_list ap; va_start(ap, fmt); qb_vsnprintf_serialize(buf, QB_LOG_MAX_LEN, fmt, ap); qb_vsnprintf_deserialize(out, QB_LOG_MAX_LEN, buf); va_end(ap); } static void format_this_up_to(char *out, size_t max_len, const char *fmt, ...) { char buf[QB_LOG_MAX_LEN]; va_list ap; va_start(ap, fmt); qb_vsnprintf_serialize(buf, max_len, fmt, ap); qb_vsnprintf_deserialize(out, QB_LOG_MAX_LEN, buf); va_end(ap); } START_TEST(test_va_serialize) { char buf[QB_LOG_MAX_LEN]; char cmp_buf[QB_LOG_MAX_LEN]; format_this(buf, "one line"); ck_assert_str_eq(buf, "one line"); format_this(buf, "p1:%p, p2:%p", format_this, buf); snprintf(cmp_buf, QB_LOG_MAX_LEN, "p1:%p, p2:%p", format_this, buf); ck_assert_str_eq(buf, cmp_buf); format_this(buf, "s1:%s, s2:%s", "Yes", "Never"); ck_assert_str_eq(buf, "s1:Yes, s2:Never"); format_this(buf, "s1:%s, s2:%s", "Yes", "Never"); ck_assert_str_eq(buf, "s1:Yes, s2:Never"); format_this(buf, "d1:%d, d2:%5i, d3:%04i", 23, 37, 84); ck_assert_str_eq(buf, "d1:23, d2: 37, d3:0084"); format_this(buf, "f1:%.5f, f2:%.2f", 23.34109, 23.34109); ck_assert_str_eq(buf, "f1:23.34109, f2:23.34"); format_this(buf, "%zd", (size_t)13140964); ck_assert_str_eq(buf, "13140964"); format_this(buf, "%jd", (intmax_t)30627823); ck_assert_str_eq(buf, "30627823"); format_this(buf, "%td", buf-cmp_buf); snprintf(cmp_buf, QB_LOG_MAX_LEN, "%td", buf-cmp_buf); ck_assert_str_eq(buf, cmp_buf); format_this(buf, ":%s:", "Hello, world!"); ck_assert_str_eq(buf, ":Hello, world!:"); format_this(buf, ":%15s:", "Hello, world!"); ck_assert_str_eq(buf, ": Hello, world!:"); format_this(buf, ":%.10s:", "Hello, world!"); ck_assert_str_eq(buf, ":Hello, wor:"); format_this(buf, ":%-10s:", "Hello, world!"); ck_assert_str_eq(buf, ":Hello, world!:"); format_this(buf, ":%-15s:", "Hello, world!"); ck_assert_str_eq(buf, ":Hello, world! :"); format_this(buf, ":%.15s:", "Hello, world!"); ck_assert_str_eq(buf, ":Hello, world!:"); format_this(buf, ":%15.10s:", "Hello, world!"); ck_assert_str_eq(buf, ": Hello, wor:"); format_this(buf, ":%-15.10s:", "Hello, world!"); ck_assert_str_eq(buf, ":Hello, wor :"); format_this(buf, ":%*d:", 8, 96); ck_assert_str_eq(buf, ": 96:"); format_this_up_to(buf, 11, "123456789____"); ck_assert_str_eq(buf, "123456789_"); format_this(buf, "Client %s.%.9s wants to fence (%s) '%s' with device '%s'", "bla", "foooooooooooooooooo", "action", "target", "hoop"); ck_assert_str_eq(buf, "Client bla.foooooooo wants to fence (action) 'target' with device 'hoop'"); format_this(buf, "Node %s now has process list: %.32x (was %.32x)", "18builder", 2, 0); ck_assert_str_eq(buf, "Node 18builder now has process list: 00000000000000000000000000000002 (was 00000000000000000000000000000000)"); } END_TEST START_TEST(test_log_stupid_inputs) { int32_t rc; /* shouldn't crash with out an init() */ qb_log_fini(); /* not init'ed */ rc = qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "bla", LOG_TRACE); ck_assert_int_eq(rc, -EINVAL); rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 2000); ck_assert_int_eq(rc, -EINVAL); qb_log(LOG_INFO, "not init'd"); qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO, __LINE__, 0, "also not init'd"); qb_log_init("test", LOG_USER, LOG_DEBUG); /* non-opened log file */ rc = qb_log_filter_ctl(21, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "bla", LOG_TRACE); ck_assert_int_eq(rc, -EBADF); rc = qb_log_ctl(21, QB_LOG_CONF_PRIORITY_BUMP, -1); ck_assert_int_eq(rc, -EBADF); /* target < 0 or >= 32 */ rc = qb_log_filter_ctl(41, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "bla", LOG_TRACE); ck_assert_int_eq(rc, -EBADF); rc = qb_log_ctl(-1, QB_LOG_CONF_PRIORITY_BUMP, -1); ck_assert_int_eq(rc, -EBADF); /* crap values to filter_ctl() */ rc = qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, NULL, LOG_INFO); ck_assert_int_eq(rc, -EINVAL); rc = qb_log_filter_ctl(QB_LOG_SYSLOG, 56, QB_LOG_FILTER_FILE, "boja", LOG_INFO); ck_assert_int_eq(rc, -EINVAL); /* crap values to ctl() */ rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, -2000); ck_assert_int_eq(rc, -EINVAL); rc = qb_log_ctl(QB_LOG_BLACKBOX, 67, 2000); ck_assert_int_eq(rc, -EINVAL); rc = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_SIZE, 2000); ck_assert_int_eq(rc, -ENOSYS); } END_TEST static char test_buf[4097]; static uint8_t test_priority; static int32_t num_msgs; static size_t last_length; /* * to test that we get what we expect. */ static void _test_logger(int32_t t, struct qb_log_callsite *cs, struct timespec *timestamp, const char *msg) { test_buf[0] = '\0'; qb_log_target_format(t, cs, timestamp, msg, test_buf); test_priority = cs->priority; num_msgs++; } static void _test_length_logger(int32_t t, struct qb_log_callsite *cs, struct timespec *timestamp, const char *msg) { strcpy(test_buf, msg); qb_log_target_format(t, cs, timestamp, msg, test_buf); test_priority = cs->priority; num_msgs++; last_length = strlen(msg); } static void log_also(void) { qb_log(LOG_INFO, "yes please"); } static void log_and_this_too(void) { qb_log(LOG_INFO, "this too please"); } static void log_it_please(void) { qb_enter(); qb_log(LOG_TRACE, "A:%d B:%d C:%d", 1, 2, 3); qb_log(LOG_DEBUG, "A:%d B:%d C:%d", 1, 2, 3); errno = EEXIST; qb_perror(LOG_WARNING, "bogus error"); errno = 0; qb_log(LOG_INFO, "A:%d B:%d C:%d", 1, 2, 3); qb_log(LOG_NOTICE, "A:%d B:%d C:%d", 1, 2, 3); qb_log(LOG_WARNING, "A:%d B:%d C:%d", 1, 2, 3); qb_log(LOG_ERR, "A:%d B:%d C:%d", 1, 2, 3); qb_leave(); } static int32_t _cust_t = -1; static void m_filter(struct qb_log_callsite *cs) { if ((cs->priority >= LOG_ALERT && cs->priority <= LOG_INFO) || cs->tags > 0) { qb_bit_set(cs->targets, _cust_t); } else { qb_bit_clear(cs->targets, _cust_t); } } START_TEST(test_log_filter_fn) { int32_t rc; qb_log_init("test", LOG_USER, LOG_EMERG); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); _cust_t = qb_log_custom_open(_test_logger, NULL, NULL, NULL); _ck_assert_int(_cust_t, >, QB_LOG_BLACKBOX); rc = qb_log_ctl(_cust_t, QB_LOG_CONF_ENABLED, QB_TRUE); ck_assert_int_eq(rc, 0); /* * test the custom filter function. * make sure qb_log, and qb_log_from_external_source are filtered. */ qb_log_filter_fn_set(m_filter); num_msgs = 0; qb_log(LOG_NOTICE, "qb_log_filter_fn_set good"); qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO, __LINE__, 0, "qb_log_filter_fn_set good"); qb_log(LOG_TRACE, "qb_log_filter_fn_set bad"); qb_log_from_external_source(__func__, __FILE__, "%s", LOG_DEBUG, __LINE__, 44, "qb_log_filter_fn_set woot"); qb_log_from_external_source(__func__, __FILE__, "%s", LOG_DEBUG, __LINE__, 0, "qb_log_filter_fn_set bad"); ck_assert_int_eq(num_msgs, 3); } END_TEST START_TEST(test_file_logging) { struct stat st; int rc, lf; unlink("test1.log"); unlink("test2.log"); qb_log_init("test", LOG_USER, LOG_DEBUG); lf = qb_log_file_open("test1.log"); rc = qb_log_filter_ctl(lf, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, __FILE__, LOG_DEBUG); ck_assert_int_eq(rc, 0); rc = qb_log_ctl(lf, QB_LOG_CONF_ENABLED, QB_TRUE); ck_assert_int_eq(rc, 0); qb_log(LOG_INFO, "write to file 1"); qb_log(LOG_INFO, "write to file 1 again"); rc = stat("test1.log", &st); ck_assert_int_eq(rc, 0); ck_assert_int_ge(st.st_size, 32); /* Test reopen with NULL arg */ rc = qb_log_file_reopen(lf, NULL); ck_assert_int_eq(rc, 0); qb_log(LOG_INFO, "write to file 1 and again"); qb_log(LOG_INFO, "write to file 1 yet again"); rc = stat("test1.log", &st); ck_assert_int_eq(rc, 0); ck_assert_int_ge(st.st_size, 64); /* Test reopen with new file */ rc = qb_log_file_reopen(lf, "test2.log"); ck_assert_int_eq(rc, 0); qb_log(LOG_INFO, "write to file 2"); qb_log(LOG_INFO, "write to file 2 again"); rc = stat("test2.log", &st); ck_assert_int_eq(rc, 0); ck_assert_int_ge(st.st_size, 32); unlink("test1.log"); unlink("test2.log"); } END_TEST START_TEST(test_timestamps) { int32_t t; int32_t rc; int a,b,c,d; qb_log_init("test", LOG_USER, LOG_EMERG); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); t = qb_log_custom_open(_test_logger, NULL, NULL, NULL); rc = qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_INFO); ck_assert_int_eq(rc, 0); /* normal timestamp */ qb_log_format_set(t, "%t %b"); rc = qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE); ck_assert_int_eq(rc, 0); qb_log(LOG_INFO, "The time now is (see left)"); rc = sscanf(test_buf+7, "%d:%d:%d.%d", &a, &b, &c, &d); ck_assert_int_eq(rc, 3); /* millisecond timestamp */ qb_log_format_set(t, "%T %b"); qb_log(LOG_INFO, "The time now is precisely (see left)"); rc = sscanf(test_buf+7, "%d:%d:%d.%d", &a, &b, &c, &d); ck_assert_int_eq(rc, 4); } END_TEST START_TEST(test_line_length) { int32_t t; int32_t rc; int i; char bigbuf[4097]; qb_log_init("test", LOG_USER, LOG_EMERG); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); t = qb_log_custom_open(_test_length_logger, NULL, NULL, NULL); rc = qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FORMAT, "*", LOG_WARNING); ck_assert_int_eq(rc, 0); qb_log_format_set(t, "[%p] %b"); rc = qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE); ck_assert_int_eq(rc, 0); rc = qb_log_ctl(t, QB_LOG_CONF_MAX_LINE_LEN, 32); ck_assert_int_eq(rc, 0); rc = qb_log_ctl(t, QB_LOG_CONF_ELLIPSIS, QB_TRUE); ck_assert_int_eq(rc, 0); /* captures last log */ memset(test_buf, 0, sizeof(test_buf)); test_priority = 0; num_msgs = 0; qb_log(LOG_ERR, "Short message"); qb_log(LOG_ERR, "This is a longer message 123456789012345678901234567890"); qb_log(LOG_ERR, "Long message with parameters %d %s", 1234, "Oh yes it is"); ck_assert_int_eq(num_msgs, 3); ck_assert_int_eq(last_length, 31); ck_assert_str_eq(test_buf+28, "..."); rc = qb_log_ctl(t, QB_LOG_CONF_ELLIPSIS, QB_FALSE); ck_assert_int_eq(rc, 0); qb_log(LOG_ERR, "Long message with parameters %d %s", 1234, "Oh yes it is"); ck_assert_str_ne(test_buf+28, "..."); /* Long lines */ num_msgs = 0; rc = qb_log_ctl(t, QB_LOG_CONF_MAX_LINE_LEN, 4096); ck_assert_int_eq(rc, 0); for (i=0; i<4096; i++) { bigbuf[i] = '0'+(i%10); } bigbuf[4096] = '\0'; qb_log(LOG_ERR, "%s", bigbuf); ck_assert_int_eq(num_msgs, 1); ck_assert_int_eq(last_length, 4095); } END_TEST START_TEST(test_log_basic) { int32_t t; int32_t rc; qb_log_init("test", LOG_USER, LOG_EMERG); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); t = qb_log_custom_open(_test_logger, NULL, NULL, NULL); rc = qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FORMAT, "Angus", LOG_WARNING); ck_assert_int_eq(rc, 0); qb_log_format_set(t, "%b"); rc = qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE); ck_assert_int_eq(rc, 0); /* captures last log */ memset(test_buf, 0, sizeof(test_buf)); test_priority = 0; num_msgs = 0; /* * test filtering by format */ qb_log(LOG_INFO, "Hello Angus, how are you?"); qb_log(LOG_WARNING, "Hello Steven, how are you?"); qb_log(LOG_ERR, "Hello Andrew, how are you?"); qb_log(LOG_ERR, "Hello Angus, how are you?"); qb_log(LOG_EMERG, "Hello Anna, how are you?"); ck_assert_int_eq(test_priority, LOG_ERR); ck_assert_int_eq(num_msgs, 1); ck_assert_str_eq(test_buf, "Hello Angus, how are you?"); /* * test filtering by file regex */ qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL, QB_LOG_FILTER_FORMAT, "*", LOG_TRACE); qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE_REGEX, "^fakefile*", LOG_DEBUG); num_msgs = 0; qb_log_from_external_source(__func__, "fakefile_logging", "%s bla", LOG_INFO, 56, 0, "filename/lineno"); qb_log_from_external_source(__func__, "do_not_log_fakefile_logging", "%s bla", LOG_INFO, 56, 0, "filename/lineno"); ck_assert_int_eq(num_msgs, 1); /* * test filtering by format regex */ qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL, QB_LOG_FILTER_FORMAT, "*", LOG_TRACE); qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FORMAT_REGEX, "^one", LOG_WARNING); num_msgs = 0; qb_log(LOG_INFO, "one two three"); qb_log(LOG_ERR, "testing one two three"); qb_log(LOG_WARNING, "one two three"); qb_log(LOG_ERR, "one two three"); qb_log(LOG_EMERG, "one two three"); ck_assert_int_eq(num_msgs, 3); /* * test filtering by function and regex */ qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL, QB_LOG_FILTER_FILE, "*", LOG_TRACE); qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FUNCTION_REGEX, "^log_.*please", LOG_WARNING); num_msgs = 0; qb_log(LOG_ERR, "try if you: log_it_please()"); log_it_please(); ck_assert_int_eq(num_msgs, 3); qb_log_filter_ctl(t, QB_LOG_FILTER_REMOVE, QB_LOG_FILTER_FUNCTION_REGEX, "log_it_please", LOG_WARNING); /* * test filtering by function */ qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL, QB_LOG_FILTER_FILE, "*", LOG_TRACE); qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FUNCTION, "log_it_please", LOG_WARNING); num_msgs = 0; qb_log(LOG_ERR, "try if you: log_it_please()"); log_it_please(); ck_assert_int_eq(num_msgs, 3); qb_log_filter_ctl(t, QB_LOG_FILTER_REMOVE, QB_LOG_FILTER_FUNCTION, "log_it_please", LOG_WARNING); qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FUNCTION, __func__, LOG_DEBUG); num_msgs = 0; log_it_please(); ck_assert_int_eq(num_msgs, 0); qb_log(LOG_DEBUG, "try if you: log_it_please()"); ck_assert_int_eq(num_msgs, 1); qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL, QB_LOG_FILTER_FILE, "*", LOG_TRACE); qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FUNCTION, "log_also,log_and_this_too", LOG_DEBUG); num_msgs = 0; log_also(); log_and_this_too(); ck_assert_int_eq(num_msgs, 2); qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL, QB_LOG_FILTER_FILE, "*", LOG_TRACE); qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "fakefile.c,"__FILE__",otherfakefile", LOG_DEBUG); /* * make sure we can pass in a null filename or function name. */ qb_log_from_external_source(__func__, NULL, "%s", LOG_INFO, __LINE__, 0, "null filename"); qb_log_from_external_source(NULL, __FILE__, "%s", LOG_INFO, __LINE__, 0, "null function"); /* check same file/lineno logs with different formats work */ num_msgs = 0; qb_log_from_external_source(__func__, __FILE__, "%s bla", LOG_INFO, 56, 0, "filename/lineno"); ck_assert_int_eq(num_msgs, 1); ck_assert_str_eq(test_buf, "filename/lineno bla"); num_msgs = 0; qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO, 56, 0, "same filename/lineno"); ck_assert_int_eq(num_msgs, 1); ck_assert_str_eq(test_buf, "same filename/lineno"); /* check filtering works on same file/lineno but different * log level. */ qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL, QB_LOG_FILTER_FILE, "*", LOG_TRACE); qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, __FILE__, LOG_INFO); num_msgs = 0; qb_log_from_external_source(__func__, __FILE__, "same filename/lineno, this level %d", LOG_INFO, 56, 0, LOG_INFO); ck_assert_int_eq(num_msgs, 1); ck_assert_str_eq(test_buf, "same filename/lineno, this level 6"); num_msgs = 0; qb_log_from_external_source(__func__, __FILE__, "same filename/lineno, this level %d", LOG_DEBUG, 56, 0, LOG_DEBUG); ck_assert_int_eq(num_msgs, 0); } END_TEST static const char *_test_tags_stringify(uint32_t tags) { if (tags == 1) { return "ONE"; } else if (tags == 8) { return "ATE"; } else { return "ANY"; } } START_TEST(test_log_format) { int32_t t; /* following size/length related equation holds in the context of use: strlen(cmp_str) = strlen(host_str) + X; X ~ 20 < sizeof(host_str) */ char host_str[256]; char cmp_str[2 * sizeof(host_str)]; qb_log_init("test", LOG_USER, LOG_DEBUG); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); t = qb_log_custom_open(_test_logger, NULL, NULL, NULL); qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE); qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_DEBUG); qb_log_format_set(t, "%p %f %b"); qb_log(LOG_DEBUG, "Angus"); ck_assert_str_eq(test_buf, "debug check_log.c Angus"); qb_log(LOG_INFO, "Angus"); ck_assert_str_eq(test_buf, "info check_log.c Angus"); qb_log(LOG_NOTICE, "Angus"); ck_assert_str_eq(test_buf, "notice check_log.c Angus"); qb_log(LOG_WARNING, "Angus"); ck_assert_str_eq(test_buf, "warning check_log.c Angus"); qb_log(LOG_ERR, "Angus"); ck_assert_str_eq(test_buf, "error check_log.c Angus"); qb_log(LOG_CRIT, "Angus"); ck_assert_str_eq(test_buf, "crit check_log.c Angus"); qb_log(LOG_ALERT, "Angus"); ck_assert_str_eq(test_buf, "alert check_log.c Angus"); qb_log(LOG_EMERG, "Angus"); ck_assert_str_eq(test_buf, "emerg check_log.c Angus"); qb_log_tags_stringify_fn_set(_test_tags_stringify); qb_log_format_set(t, "%g %b"); qb_logt(LOG_INFO, 0, "Angus"); ck_assert_str_eq(test_buf, "ANY Angus"); qb_logt(LOG_INFO, 1, "Angus"); ck_assert_str_eq(test_buf, "ONE Angus"); qb_logt(LOG_INFO, 5, "Angus"); ck_assert_str_eq(test_buf, "ANY Angus"); qb_logt(LOG_INFO, 8, "Angus"); ck_assert_str_eq(test_buf, "ATE Angus"); qb_log_format_set(t, "%-15f %b"); qb_log(LOG_WARNING, "Andrew"); ck_assert_str_eq(test_buf, " check_log.c Andrew"); qb_log_tags_stringify_fn_set(NULL); gethostname(host_str, sizeof(host_str)); host_str[sizeof(host_str) - 1] = '\0'; qb_log_format_set(t, "%P %H %N %b"); qb_log(LOG_INFO, "Angus"); snprintf(cmp_str, sizeof(cmp_str), "%d %s test Angus", getpid(), host_str); ck_assert_str_eq(test_buf, cmp_str); qb_log_format_set(t, "%3N %H %P %b"); qb_log(LOG_INFO, "Angus"); snprintf(cmp_str, sizeof(cmp_str), "tes %s %d Angus", host_str, getpid()); ck_assert_str_eq(test_buf, cmp_str); } END_TEST START_TEST(test_log_enable) { int32_t t; int32_t state; qb_log_init("test", LOG_USER, LOG_DEBUG); state = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_STATE_GET, 0); ck_assert_int_eq(state, QB_LOG_STATE_ENABLED); state = qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_STATE_GET, 0); ck_assert_int_eq(state, QB_LOG_STATE_DISABLED); state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0); ck_assert_int_eq(state, QB_LOG_STATE_DISABLED); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); state = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_STATE_GET, 0); ck_assert_int_eq(state, QB_LOG_STATE_DISABLED); t = qb_log_custom_open(_test_logger, NULL, NULL, NULL); qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE); qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_DEBUG); qb_log_format_set(t, "%b"); qb_log(LOG_DEBUG, "Hello"); ck_assert_str_eq(test_buf, "Hello"); num_msgs = 0; qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_FALSE); qb_log(LOG_DEBUG, "Goodbye"); ck_assert_int_eq(num_msgs, 0); qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE); qb_log(LOG_DEBUG, "Hello again"); ck_assert_int_eq(num_msgs, 1); ck_assert_str_eq(test_buf, "Hello again"); } END_TEST #define ITERATIONS 100000 static void *thr_send_logs_2(void *ctx) { int32_t i; printf("%s\n", __func__); for (i = 0; i < ITERATIONS; i++) { qb_log(LOG_INFO, "bla bla"); qb_log(LOG_INFO, "blue blue"); qb_log(LOG_INFO, "bra bra"); qb_log(LOG_INFO, "bro bro"); qb_log(LOG_INFO, "brown brown"); qb_log(LOG_INFO, "booo booo"); qb_log(LOG_INFO, "bogus bogus"); qb_log(LOG_INFO, "bungu bungu"); } return (NULL); } static void *thr_send_logs_1(void *ctx) { int32_t i; printf("%s\n", __func__); for (i = 0; i < ITERATIONS; i++) { qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO, __LINE__, 0, "foo soup"); qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO, __LINE__, 0, "fungus soup"); qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO, __LINE__, 0, "fruity soup"); qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO, __LINE__, 0, "free soup"); qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO, __LINE__, 0, "frot soup"); qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO, __LINE__, 0, "fresh soup"); qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO, __LINE__, 0, "fattening soup"); } return (NULL); } #define THREADS 4 START_TEST(test_log_threads) { pthread_t threads[THREADS]; pthread_attr_t thread_attr[THREADS]; int32_t i; int32_t rc; int32_t lf; void *retval; qb_log_init("test", LOG_USER, LOG_DEBUG); lf = qb_log_file_open("threads.log"); rc = qb_log_filter_ctl(lf, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, __FILE__, LOG_DEBUG); ck_assert_int_eq(rc, 0); qb_log_format_set(lf, "[%p] [%l] %b"); rc = qb_log_ctl(lf, QB_LOG_CONF_ENABLED, QB_TRUE); ck_assert_int_eq(rc, 0); rc = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); ck_assert_int_eq(rc, 0); for (i = 0; i < THREADS/2; i++) { pthread_attr_init(&thread_attr[i]); pthread_attr_setdetachstate(&thread_attr[i], PTHREAD_CREATE_JOINABLE); pthread_create(&threads[i], &thread_attr[i], thr_send_logs_1, NULL); } for (i = THREADS/2; i < THREADS; i++) { pthread_attr_init(&thread_attr[i]); pthread_attr_setdetachstate(&thread_attr[i], PTHREAD_CREATE_JOINABLE); pthread_create(&threads[i], &thread_attr[i], thr_send_logs_2, NULL); } for (i = 0; i < THREADS; i++) { pthread_join(threads[i], &retval); } } END_TEST START_TEST(test_log_long_msg) { int lpc; int rc; int i, max = 1000; char *buffer = calloc(1, max); qb_log_init("test", LOG_USER, LOG_DEBUG); rc = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); ck_assert_int_eq(rc, 0); rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 1024); ck_assert_int_eq(rc, 0); rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); ck_assert_int_eq(rc, 0); rc = qb_log_filter_ctl(QB_LOG_BLACKBOX, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_TRACE); ck_assert_int_eq(rc, 0); for (lpc = 500; lpc < max; lpc++) { lpc++; for(i = 0; i < max; i++) { buffer[i] = 'a' + (i % 10); } buffer[lpc%600] = 0; qb_log(LOG_INFO, "Message %d %d - %s", lpc, lpc%600, buffer); } rc = qb_log_blackbox_write_to_file("blackbox.dump"); ck_assert_int_gt(rc, 0); rc = qb_log_blackbox_print_from_file("blackbox.dump"); ck_assert_int_le(rc, 0); unlink("blackbox.dump"); qb_log_fini(); } END_TEST START_TEST(test_threaded_logging) { int32_t t; int32_t rc; qb_log_init("test", LOG_USER, LOG_EMERG); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); t = qb_log_custom_open(_test_logger, NULL, NULL, NULL); rc = qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_INFO); ck_assert_int_eq(rc, 0); qb_log_format_set(t, "%b"); rc = qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE); ck_assert_int_eq(rc, 0); rc = qb_log_ctl(t, QB_LOG_CONF_THREADED, QB_TRUE); ck_assert_int_eq(rc, 0); qb_log_thread_start(); memset(test_buf, 0, sizeof(test_buf)); test_priority = 0; num_msgs = 0; qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__); qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__); qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__); qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__); qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__); qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__); qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__); qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__); qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__); qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__); qb_log_fini(); ck_assert_int_eq(num_msgs, 10); } END_TEST #ifdef HAVE_PTHREAD_SETSCHEDPARAM START_TEST(test_threaded_logging_bad_sched_params) { int32_t t; int32_t rc; qb_log_init("test", LOG_USER, LOG_EMERG); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); t = qb_log_custom_open(_test_logger, NULL, NULL, NULL); rc = qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_INFO); ck_assert_int_eq(rc, 0); qb_log_format_set(t, "%b"); rc = qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE); ck_assert_int_eq(rc, 0); rc = qb_log_ctl(t, QB_LOG_CONF_THREADED, QB_TRUE); ck_assert_int_eq(rc, 0); #if defined(SCHED_RR) #define QB_SCHED SCHED_RR #elif defined(SCHED_FIFO) #define QB_SCHED SCHED_FIFO #else #define QB_SCHED (-1) #endif rc = qb_log_thread_priority_set(QB_SCHED, -1); ck_assert_int_eq(rc, 0); rc = qb_log_thread_start(); ck_assert_int_ne(rc, 0); qb_log_fini(); } END_TEST #endif START_TEST(test_extended_information) { int32_t t; int32_t rc; int extended; qb_log_init("test", LOG_USER, LOG_DEBUG); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); t = qb_log_custom_open(_test_logger, NULL, NULL, NULL); _ck_assert_int(t, >, QB_LOG_STDOUT); qb_log_format_set(t, "%b"); rc = qb_log_filter_fn_set(NULL); ck_assert_int_eq(rc, 0); rc = qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL, QB_LOG_FILTER_FILE, "*", LOG_TRACE); ck_assert_int_eq(rc, 0); rc = qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL, QB_LOG_FILTER_FORMAT, "*", LOG_TRACE); ck_assert_int_eq(rc, 0); rc = qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FORMAT, "*", LOG_TRACE); ck_assert_int_eq(rc, 0); rc = qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE); ck_assert_int_eq(rc, 0); for (extended = QB_FALSE; extended <= QB_TRUE; ++extended) { rc = qb_log_ctl(t, QB_LOG_CONF_EXTENDED, extended); ck_assert_int_eq(rc, 0); num_msgs = 0; memset(test_buf, 0, sizeof(test_buf)); qb_log(LOG_ERR, "message with no extended information"); ck_assert_str_eq(test_buf, "message with no extended information"); memset(test_buf, 0, sizeof(test_buf)); qb_log(LOG_ERR, "message with empty extended information "QB_XS); ck_assert_str_eq(test_buf, "message with empty extended information "); memset(test_buf, 0, sizeof(test_buf)); qb_log(LOG_ERR, QB_XS" message with only extended information"); ck_assert_str_eq(test_buf, extended? "| message with only extended information" : ""); memset(test_buf, 0, sizeof(test_buf)); qb_log(LOG_ERR, "message with extended information "QB_XS" (namely this)"); ck_assert_str_eq(test_buf, extended? "message with extended information | (namely this)" : "message with extended information "); ck_assert_int_eq(num_msgs, (extended? 4 : 3)); } qb_log_fini(); } END_TEST static const char *tagtest_stringify_tag(uint32_t tag) { static char buf[32]; sprintf(buf, "%5" PRIu32, tag); return buf; } START_TEST(test_zero_tags) { int32_t rc; int32_t t; qb_log_init("test", LOG_USER, LOG_EMERG); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); t = qb_log_custom_open(_test_logger, NULL, NULL, NULL); rc = qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_INFO); ck_assert_int_eq(rc, 0); qb_log_format_set(t, "[%g] %b"); qb_log_tags_stringify_fn_set(tagtest_stringify_tag); rc = qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE); ck_assert_int_eq(rc, 0); qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_TRACE); qb_log_from_external_source("function", "filename", "%s: %d", LOG_DEBUG, 356, 2, "testlog", 2); ck_assert_str_eq(test_buf, "[ 2] testlog: 2"); qb_log_from_external_source("function", "filename", "%s: %d", LOG_DEBUG, 356, 0, "testlog", 0); ck_assert_str_eq(test_buf, "[ 2] testlog: 0"); qb_log_fini(); } END_TEST #ifdef USE_JOURNAL START_TEST(test_journal) { int rc; const char *msg; size_t len; pid_t log_pid; sd_journal *jnl; int count = 0; const char *msgid="f77379a8490b408bbe5f6940505a777b"; qb_log_init("check_log", LOG_USER, LOG_DEBUG); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_TRUE); rc = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_USE_JOURNAL, 1); ck_assert_int_eq(rc, 0); qb_log2(msgid, LOG_ERR, "Test message 1 from libqb"); qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_USE_JOURNAL, 1); ck_assert_int_eq(rc, -EINVAL); sleep(1); /* Check it reached the journal */ rc = sd_journal_open(&jnl, 0); ck_assert_int_eq(rc, 0); rc = sd_journal_seek_tail(jnl); ck_assert_int_eq(rc, 0); SD_JOURNAL_FOREACH_BACKWARDS(jnl) { rc = sd_journal_get_data(jnl, "_PID", (const void **)&msg, &len); ck_assert_int_eq(rc, 0); sscanf(msg, "_PID=%d", &log_pid); fprintf(stderr, "PID message = '%s' - pid = %d (pid=%d, parent=%d)\n", msg, log_pid, getpid(), getppid()); if (log_pid == getpid()) { rc = sd_journal_get_data(jnl, "MESSAGE", (const void **)&msg, &len); ck_assert_int_eq(rc, 0); rc = sd_journal_get_data(jnl, "MESSAGE_ID", (const void **)&msg, &len); ck_assert_int_eq(rc, 0); ck_assert_str_eq(msg+11, msgid); break; } if (++count > 20) { break; } } sd_journal_close(jnl); ck_assert_int_lt(count, 20); } END_TEST #else START_TEST(test_syslog) { int rc; qb_log_init("flip", LOG_USER, LOG_INFO); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_TRUE); qb_log(LOG_ERR, "first as flip"); ck_assert_int_eq(_syslog_opened, 1); ck_assert_str_eq(_syslog_ident, "flip"); qb_log_ctl2(QB_LOG_SYSLOG, QB_LOG_CONF_IDENT, QB_LOG_CTL2_S("flop")); qb_log(LOG_ERR, "second as flop"); ck_assert_str_eq(_syslog_ident, "flop"); /* This test only runs if USE_JOURNAL is undefined, so should always fail */ rc = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_USE_JOURNAL, 1); ck_assert_int_eq(rc, -EOPNOTSUPP); qb_log_fini(); } END_TEST #endif static Suite * log_suite(void) { TCase *tc; Suite *s = suite_create("logging"); - add_tcase(s, tc, test_va_serialize); - add_tcase(s, tc, test_log_stupid_inputs); - add_tcase(s, tc, test_log_basic); - add_tcase(s, tc, test_log_format); - add_tcase(s, tc, test_log_enable); + add_tcase(s, tc, test_va_serialize, 0); + add_tcase(s, tc, test_log_stupid_inputs, 0); + add_tcase(s, tc, test_log_basic, 0); + add_tcase(s, tc, test_log_format, 0); + add_tcase(s, tc, test_log_enable, 0); add_tcase(s, tc, test_log_threads, 360); - add_tcase(s, tc, test_log_long_msg); - add_tcase(s, tc, test_log_filter_fn); - add_tcase(s, tc, test_threaded_logging); - add_tcase(s, tc, test_line_length); - add_tcase(s, tc, test_file_logging); + add_tcase(s, tc, test_log_long_msg, 0); + add_tcase(s, tc, test_log_filter_fn, 0); + add_tcase(s, tc, test_threaded_logging, 0); + add_tcase(s, tc, test_line_length, 0); + add_tcase(s, tc, test_file_logging, 0); #ifdef HAVE_PTHREAD_SETSCHEDPARAM - add_tcase(s, tc, test_threaded_logging_bad_sched_params); + add_tcase(s, tc, test_threaded_logging_bad_sched_params, 0); #endif - add_tcase(s, tc, test_timestamps); - add_tcase(s, tc, test_extended_information); - add_tcase(s, tc, test_zero_tags); + add_tcase(s, tc, test_timestamps, 0); + add_tcase(s, tc, test_extended_information, 0); + add_tcase(s, tc, test_zero_tags, 0); /* * You can still use syslog and journal in a normal application, * but the syslog_override code doesn't work when -lsystemd is present */ #ifdef USE_JOURNAL - add_tcase(s, tc, test_journal); + add_tcase(s, tc, test_journal, 0); #else - add_tcase(s, tc, test_syslog); + add_tcase(s, tc, test_syslog, 0); #endif return s; } int32_t main(void) { int32_t number_failed; Suite *s = log_suite(); SRunner *sr = srunner_create(s); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/check_loop.c b/tests/check_loop.c index b20cd85..c537e00 100644 --- a/tests/check_loop.c +++ b/tests/check_loop.c @@ -1,833 +1,833 @@ /* * Copyright (c) 2010 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * This file is part of libqb. * * libqb is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libqb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libqb. If not, see . */ #include "os_base.h" #include "check_common.h" #include #include #include #include static int32_t job_1_run_count = 0; static int32_t job_2_run_count = 0; static int32_t job_3_run_count = 0; static int32_t job_order_1 = 1; static int32_t job_order_2 = 2; static int32_t job_order_3 = 3; static int32_t job_order_4 = 4; static int32_t job_order_5 = 5; static int32_t job_order_6 = 6; static int32_t job_order_7 = 7; static int32_t job_order_8 = 8; static int32_t job_order_9 = 9; static int32_t job_order_10 = 10; static int32_t job_order_11 = 11; static int32_t job_order_12 = 12; static int32_t job_order_13 = 13; static void job_1(void *data) { job_1_run_count++; } static void job_order_check(void *data) { int32_t * order = (int32_t *)data; job_1_run_count++; ck_assert_int_eq(job_1_run_count, *order); if (job_1_run_count == 1) { qb_loop_job_add(NULL, QB_LOOP_MED, &job_order_10, job_order_check); qb_loop_job_add(NULL, QB_LOOP_MED, &job_order_11, job_order_check); qb_loop_job_add(NULL, QB_LOOP_MED, &job_order_12, job_order_check); qb_loop_job_add(NULL, QB_LOOP_MED, &job_order_13, job_order_check); } else if (job_1_run_count >= 13) { qb_loop_stop(NULL); } } static void job_stop(void *data) { qb_loop_t *l = (qb_loop_t *)data; job_3_run_count++; qb_loop_stop(l); } static void job_2(void *data) { int32_t res; qb_loop_t *l = (qb_loop_t *)data; job_2_run_count++; res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_stop); ck_assert_int_eq(res, 0); } static void job_1_r(void *data) { int32_t res; qb_loop_t *l = (qb_loop_t *)data; job_1_run_count++; res = qb_loop_job_add(l, QB_LOOP_MED, data, job_2); ck_assert_int_eq(res, 0); } static void job_1_add_nuts(void *data) { int32_t res; qb_loop_t *l = (qb_loop_t *)data; job_1_run_count++; res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_MED, data, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_MED, data, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_MED, data, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_MED, data, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_LOW, data, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_LOW, data, job_1); ck_assert_int_eq(res, 0); if (job_1_run_count < 500) { res = qb_loop_job_add(l, QB_LOOP_LOW, data, job_1_add_nuts); ck_assert_int_eq(res, 0); } else { res = qb_loop_job_add(l, QB_LOOP_LOW, data, job_stop); ck_assert_int_eq(res, 0); } ck_assert_int_eq(res, 0); } START_TEST(test_loop_job_input) { int32_t res; qb_loop_t *l; res = qb_loop_job_add(NULL, QB_LOOP_LOW, NULL, job_2); ck_assert_int_eq(res, -EINVAL); l = qb_loop_create(); ck_assert(l != NULL); res = qb_loop_job_add(NULL, QB_LOOP_LOW, NULL, job_2); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, 89, NULL, job_2); ck_assert_int_eq(res, -EINVAL); res = qb_loop_job_add(l, QB_LOOP_LOW, NULL, NULL); ck_assert_int_eq(res, -EINVAL); qb_loop_destroy(l); } END_TEST START_TEST(test_loop_job_1) { int32_t res; qb_loop_t *l = qb_loop_create(); ck_assert(l != NULL); res = qb_loop_job_add(l, QB_LOOP_LOW, NULL, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_LOW, l, job_stop); ck_assert_int_eq(res, 0); qb_loop_run(l); ck_assert_int_eq(job_1_run_count, 1); qb_loop_destroy(l); } END_TEST START_TEST(test_loop_job_4) { int32_t res; qb_loop_t *l = qb_loop_create(); ck_assert(l != NULL); res = qb_loop_job_add(l, QB_LOOP_LOW, l, job_1_r); ck_assert_int_eq(res, 0); qb_loop_run(l); ck_assert_int_eq(job_1_run_count, 1); ck_assert_int_eq(job_2_run_count, 1); ck_assert_int_eq(job_3_run_count, 1); qb_loop_destroy(l); } END_TEST START_TEST(test_loop_job_nuts) { int32_t res; qb_loop_t *l = qb_loop_create(); ck_assert(l != NULL); res = qb_loop_job_add(l, QB_LOOP_LOW, l, job_1_add_nuts); ck_assert_int_eq(res, 0); qb_loop_run(l); ck_assert(job_1_run_count >= 500); qb_loop_destroy(l); } END_TEST START_TEST(test_loop_job_order) { int32_t res; qb_loop_t *l = qb_loop_create(); ck_assert(l != NULL); job_1_run_count = 0; res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_1, job_order_check); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_2, job_order_check); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_3, job_order_check); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_4, job_order_check); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_5, job_order_check); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_6, job_order_check); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_7, job_order_check); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_8, job_order_check); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_9, job_order_check); ck_assert_int_eq(res, 0); qb_loop_run(l); qb_loop_destroy(l); } END_TEST static qb_util_stopwatch_t *rl_sw; #define RATE_LIMIT_RUNTIME_SEC 3 static void job_add_self(void *data) { int32_t res; uint64_t elapsed1; qb_loop_t *l = (qb_loop_t *)data; job_1_run_count++; qb_util_stopwatch_stop(rl_sw); elapsed1 = qb_util_stopwatch_us_elapsed_get(rl_sw); if (elapsed1 > (RATE_LIMIT_RUNTIME_SEC * QB_TIME_US_IN_SEC)) { /* run for 3 seconds */ qb_loop_stop(l); return; } res = qb_loop_job_add(l, QB_LOOP_MED, data, job_add_self); ck_assert_int_eq(res, 0); } START_TEST(test_job_rate_limit) { int32_t res; qb_loop_t *l = qb_loop_create(); ck_assert(l != NULL); rl_sw = qb_util_stopwatch_create(); ck_assert(rl_sw != NULL); qb_util_stopwatch_start(rl_sw); res = qb_loop_job_add(l, QB_LOOP_MED, l, job_add_self); ck_assert_int_eq(res, 0); qb_loop_run(l); /* * the test is to confirm that a single job does not run away * and cause cpu spin. We are going to say that a spin is more than * one job per 50ms if there is only one job pending in the loop. */ _ck_assert_int(job_1_run_count, <, (RATE_LIMIT_RUNTIME_SEC * (QB_TIME_MS_IN_SEC/50)) + 10); qb_loop_destroy(l); qb_util_stopwatch_free(rl_sw); } END_TEST static void job_stop_and_del_1(void *data) { int32_t res; qb_loop_t *l = (qb_loop_t *)data; job_3_run_count++; res = qb_loop_job_del(l, QB_LOOP_MED, l, job_1); ck_assert_int_eq(res, 0); qb_loop_stop(l); } START_TEST(test_job_add_del) { int32_t res; qb_loop_t *l = qb_loop_create(); ck_assert(l != NULL); res = qb_loop_job_add(l, QB_LOOP_MED, l, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_del(l, QB_LOOP_MED, l, job_1); ck_assert_int_eq(res, 0); job_1_run_count = 0; job_3_run_count = 0; res = qb_loop_job_add(l, QB_LOOP_MED, l, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_HIGH, l, job_stop_and_del_1); ck_assert_int_eq(res, 0); qb_loop_run(l); ck_assert_int_eq(job_1_run_count, 0); ck_assert_int_eq(job_3_run_count, 1); qb_loop_destroy(l); } END_TEST static Suite *loop_job_suite(void) { TCase *tc; Suite *s = suite_create("loop_job"); - add_tcase(s, tc, test_loop_job_input); - add_tcase(s, tc, test_loop_job_1); - add_tcase(s, tc, test_loop_job_4); + add_tcase(s, tc, test_loop_job_input, 0); + add_tcase(s, tc, test_loop_job_1, 0); + add_tcase(s, tc, test_loop_job_4, 0); add_tcase(s, tc, test_loop_job_nuts, 5); add_tcase(s, tc, test_job_rate_limit, 5); - add_tcase(s, tc, test_job_add_del); - add_tcase(s, tc, test_loop_job_order); + add_tcase(s, tc, test_job_add_del, 0); + add_tcase(s, tc, test_loop_job_order, 0); return s; } /* * ----------------------------------------------------------------------- * Timers */ static qb_loop_timer_handle test_th; static qb_loop_timer_handle test_th2; static void check_time_left(void *data) { qb_loop_t *l = (qb_loop_t *)data; /* NOTE: We are checking the 'stop_loop' timer here, not our own */ uint64_t abs_time = qb_loop_timer_expire_time_get(l, test_th); uint64_t rel_time = qb_loop_timer_expire_time_remaining(l, test_th); ck_assert(abs_time > 0ULL); ck_assert(rel_time > 0ULL); ck_assert(abs_time > rel_time); ck_assert(rel_time <= 500*QB_TIME_NS_IN_MSEC); } START_TEST(test_loop_timer_input) { int32_t res; qb_loop_t *l; res = qb_loop_timer_add(NULL, QB_LOOP_LOW, 5*QB_TIME_NS_IN_MSEC, NULL, job_2, &test_th); ck_assert_int_eq(res, -EINVAL); l = qb_loop_create(); ck_assert(l != NULL); res = qb_loop_timer_add(NULL, QB_LOOP_LOW, 5*QB_TIME_NS_IN_MSEC, NULL, job_2, &test_th); ck_assert_int_eq(res, 0); res = qb_loop_timer_add(l, QB_LOOP_LOW, 5*QB_TIME_NS_IN_MSEC, l, NULL, &test_th); ck_assert_int_eq(res, -EINVAL); qb_loop_destroy(l); } END_TEST static void one_shot_tmo(void * data) { static int32_t been_here = QB_FALSE; ck_assert_int_eq(been_here, QB_FALSE); been_here = QB_TRUE; } static qb_loop_timer_handle reset_th; static int32_t reset_timer_step = 0; static void reset_one_shot_tmo(void*data) { int32_t res; qb_loop_t *l = data; if (reset_timer_step == 0) { res = qb_loop_timer_del(l, reset_th); ck_assert_int_eq(res, -EINVAL); res = qb_loop_timer_is_running(l, reset_th); ck_assert_int_eq(res, QB_FALSE); res = qb_loop_timer_add(l, QB_LOOP_LOW, 8*QB_TIME_NS_IN_MSEC, l, reset_one_shot_tmo, &reset_th); ck_assert_int_eq(res, 0); } reset_timer_step++; } START_TEST(test_loop_timer_basic) { int32_t res; qb_loop_t *l = qb_loop_create(); ck_assert(l != NULL); res = qb_loop_timer_add(l, QB_LOOP_LOW, 5*QB_TIME_NS_IN_MSEC, l, one_shot_tmo, &test_th); ck_assert_int_eq(res, 0); res = qb_loop_timer_is_running(l, test_th); ck_assert_int_eq(res, QB_TRUE); res = qb_loop_timer_add(l, QB_LOOP_LOW, 7*QB_TIME_NS_IN_MSEC, l, reset_one_shot_tmo, &reset_th); ck_assert_int_eq(res, 0); res = qb_loop_timer_add(l, QB_LOOP_LOW, 500*QB_TIME_NS_IN_MSEC, l, job_stop, &test_th); ck_assert_int_eq(res, 0); res = qb_loop_timer_add(l, QB_LOOP_HIGH, 5*QB_TIME_NS_IN_MSEC, l, check_time_left, &test_th2); ck_assert_int_eq(res, 0); qb_loop_run(l); ck_assert_int_eq(reset_timer_step, 2); qb_loop_destroy(l); } END_TEST static void *loop_timer_thread(void *arg) { int res; qb_loop_t *l = (qb_loop_t *)arg; qb_loop_timer_handle test_tht; res = qb_loop_timer_add(l, QB_LOOP_LOW, 10*QB_TIME_NS_IN_MSEC, l, one_shot_tmo, &test_tht); ck_assert_int_eq(res, 0); res = qb_loop_timer_is_running(l, test_tht); ck_assert_int_eq(res, QB_TRUE); sleep(5); return (void *)0; } /* This test will probably never fail (unless something really bad happens) but is useful for running under helgrind to find threading issues */ START_TEST(test_loop_timer_threads) { int32_t res; pthread_t thr; qb_loop_t *l = qb_loop_create(); ck_assert(l != NULL); res = pthread_create(&thr, NULL, loop_timer_thread, l); res = qb_loop_timer_add(l, QB_LOOP_LOW, 7*QB_TIME_NS_IN_MSEC, l, reset_one_shot_tmo, &reset_th); ck_assert_int_eq(res, 0); res = qb_loop_timer_add(l, QB_LOOP_LOW, 500*QB_TIME_NS_IN_MSEC, l, job_stop, &test_th); ck_assert_int_eq(res, 0); res = qb_loop_timer_add(l, QB_LOOP_HIGH, 5*QB_TIME_NS_IN_MSEC, l, check_time_left, &test_th2); ck_assert_int_eq(res, 0); qb_loop_run(l); ck_assert_int_eq(reset_timer_step, 2); pthread_join(thr, NULL); qb_loop_destroy(l); } END_TEST struct qb_stop_watch { uint64_t start; uint64_t end; qb_loop_t *l; uint64_t ns_timer; int64_t total; int32_t count; int32_t killer; qb_loop_timer_handle th; }; static void stop_watch_tmo(void*data) { struct qb_stop_watch *sw = (struct qb_stop_watch *)data; float per; int64_t diff; sw->end = qb_util_nano_current_get(); diff = sw->end - sw->start; if (diff < sw->ns_timer) { printf("timer expired early! by %"PRIi64"\n", (int64_t)(sw->ns_timer - diff)); } ck_assert(diff >= sw->ns_timer); sw->total += diff; sw->total -= sw->ns_timer; sw->start = sw->end; sw->count++; if (sw->count < 50) { qb_loop_timer_add(sw->l, QB_LOOP_LOW, sw->ns_timer, data, stop_watch_tmo, &sw->th); } else { per = ((sw->total * 100) / sw->count) / (float)sw->ns_timer; printf("average error for %"PRIu64" ns timer is %"PRIi64" (ns) (%f)\n", sw->ns_timer, (int64_t)(sw->total/sw->count), per); if (sw->killer) { qb_loop_stop(sw->l); } } } static void start_timer(qb_loop_t *l, struct qb_stop_watch *sw, uint64_t timeout, int32_t killer) { int32_t res; sw->l = l; sw->count = 0; sw->total = 0; sw->killer = killer; sw->ns_timer = timeout; sw->start = qb_util_nano_current_get(); res = qb_loop_timer_add(sw->l, QB_LOOP_LOW, sw->ns_timer, sw, stop_watch_tmo, &sw->th); ck_assert_int_eq(res, 0); } START_TEST(test_loop_timer_precision) { int32_t i; uint64_t tmo; struct qb_stop_watch sw[11]; qb_loop_t *l = qb_loop_create(); ck_assert(l != NULL); for (i = 0; i < 10; i++) { tmo = ((1 + i * 9) * QB_TIME_NS_IN_MSEC) + 500000; start_timer(l, &sw[i], tmo, QB_FALSE); } start_timer(l, &sw[i], 100 * QB_TIME_NS_IN_MSEC, QB_TRUE); qb_loop_run(l); qb_loop_destroy(l); } END_TEST static int expire_leak_counter = 0; #define EXPIRE_NUM_RUNS 10 static int expire_leak_runs = 0; static void empty_func_tmo(void*data) { expire_leak_counter++; } static void stop_func_tmo(void*data) { qb_loop_t *l = (qb_loop_t *)data; qb_log(LOG_DEBUG, "expire_leak_counter:%d", expire_leak_counter); qb_loop_stop(l); } static void next_func_tmo(void*data) { qb_loop_t *l = (qb_loop_t *)data; int32_t i; uint64_t tmo; uint64_t max_tmo = 0; qb_loop_timer_handle th; qb_log(LOG_DEBUG, "expire_leak_counter:%d", expire_leak_counter); for (i = 0; i < 300; i++) { tmo = ((1 + i) * QB_TIME_NS_IN_MSEC) + 500000; qb_loop_timer_add(l, QB_LOOP_LOW, tmo, NULL, empty_func_tmo, &th); qb_loop_timer_add(l, QB_LOOP_MED, tmo, NULL, empty_func_tmo, &th); qb_loop_timer_add(l, QB_LOOP_HIGH, tmo, NULL, empty_func_tmo, &th); max_tmo = QB_MAX(max_tmo, tmo); } expire_leak_runs++; if (expire_leak_runs == EXPIRE_NUM_RUNS) { qb_loop_timer_add(l, QB_LOOP_LOW, max_tmo, l, stop_func_tmo, &th); } else { qb_loop_timer_add(l, QB_LOOP_LOW, max_tmo, l, next_func_tmo, &th); } } /* * make sure that file descriptors don't get leaked with no qb_loop_timer_del() */ START_TEST(test_loop_timer_expire_leak) { int32_t i; uint64_t tmo; uint64_t max_tmo = 0; qb_loop_timer_handle th; qb_loop_t *l = qb_loop_create(); ck_assert(l != NULL); expire_leak_counter = 0; for (i = 0; i < 300; i++) { tmo = ((1 + i) * QB_TIME_NS_IN_MSEC) + 500000; qb_loop_timer_add(l, QB_LOOP_LOW, tmo, NULL, empty_func_tmo, &th); qb_loop_timer_add(l, QB_LOOP_MED, tmo, NULL, empty_func_tmo, &th); qb_loop_timer_add(l, QB_LOOP_HIGH, tmo, NULL, empty_func_tmo, &th); max_tmo = QB_MAX(max_tmo, tmo); } qb_loop_timer_add(l, QB_LOOP_LOW, max_tmo, l, next_func_tmo, &th); expire_leak_runs = 1; qb_loop_run(l); ck_assert_int_eq(expire_leak_counter, 300*3* EXPIRE_NUM_RUNS); qb_loop_destroy(l); } END_TEST static int received_signum = 0; static int received_sigs = 0; static int32_t sig_handler(int32_t rsignal, void *data) { qb_loop_t *l = (qb_loop_t *)data; qb_log(LOG_DEBUG, "caught signal %d", rsignal); received_signum = rsignal; received_sigs++; qb_loop_job_add(l, QB_LOOP_LOW, NULL, job_stop); return 0; } START_TEST(test_loop_sig_handling) { qb_loop_signal_handle handle; qb_loop_t *l = qb_loop_create(); ck_assert(l != NULL); qb_loop_signal_add(l, QB_LOOP_HIGH, SIGINT, l, sig_handler, &handle); qb_loop_signal_add(l, QB_LOOP_HIGH, SIGTERM, l, sig_handler, &handle); qb_loop_signal_add(l, QB_LOOP_HIGH, SIGQUIT, l, sig_handler, &handle); kill(getpid(), SIGINT); qb_loop_run(l); ck_assert_int_eq(received_signum, SIGINT); kill(getpid(), SIGQUIT); qb_loop_run(l); ck_assert_int_eq(received_signum, SIGQUIT); qb_loop_destroy(l); } END_TEST /* Globals for this test only */ static int our_signal_called = 0; static qb_loop_t *this_l; static void handle_nonqb_signal(int num) { our_signal_called = 1; qb_loop_job_add(this_l, QB_LOOP_LOW, NULL, job_stop); } START_TEST(test_loop_dont_override_other_signals) { qb_loop_signal_handle handle; this_l = qb_loop_create(); ck_assert(this_l != NULL); signal(SIGUSR1, handle_nonqb_signal); qb_loop_signal_add(this_l, QB_LOOP_HIGH, SIGINT, this_l, sig_handler, &handle); kill(getpid(), SIGUSR1); qb_loop_run(this_l); ck_assert_int_eq(our_signal_called, 1); qb_loop_destroy(this_l); } END_TEST START_TEST(test_loop_sig_only_get_one) { int res; qb_loop_signal_handle handle; qb_loop_t *l = qb_loop_create(); ck_assert(l != NULL); /* make sure we only get one call to the handler * don't assume we are going to exit the loop. */ received_sigs = 0; qb_loop_signal_add(l, QB_LOOP_LOW, SIGINT, l, sig_handler, &handle); res = qb_loop_job_add(l, QB_LOOP_MED, NULL, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_HIGH, NULL, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_MED, NULL, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_HIGH, NULL, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_HIGH, NULL, job_1); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_MED, NULL, job_1); ck_assert_int_eq(res, 0); kill(getpid(), SIGINT); qb_loop_run(l); ck_assert_int_eq(received_signum, SIGINT); ck_assert_int_eq(received_sigs, 1); qb_loop_destroy(l); } END_TEST static qb_loop_signal_handle sig_hdl; static void job_rm_sig_handler(void *data) { int res; qb_loop_t *l = (qb_loop_t *)data; res = qb_loop_signal_del(l, sig_hdl); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_LOW, NULL, job_stop); ck_assert_int_eq(res, 0); } START_TEST(test_loop_sig_delete) { int res; qb_loop_t *l = qb_loop_create(); ck_assert(l != NULL); /* make sure we can remove a signal job from the job queue. */ received_sigs = 0; received_signum = 0; res = qb_loop_signal_add(l, QB_LOOP_MED, SIGINT, l, sig_handler, &sig_hdl); ck_assert_int_eq(res, 0); res = qb_loop_job_add(l, QB_LOOP_HIGH, NULL, job_rm_sig_handler); ck_assert_int_eq(res, 0); kill(getpid(), SIGINT); qb_loop_run(l); ck_assert_int_eq(received_sigs, 0); ck_assert_int_eq(received_signum, 0); qb_loop_destroy(l); } END_TEST static Suite * loop_timer_suite(void) { TCase *tc; Suite *s = suite_create("loop_timers"); - add_tcase(s, tc, test_loop_timer_input); + add_tcase(s, tc, test_loop_timer_input, 0); add_tcase(s, tc, test_loop_timer_basic, 30); add_tcase(s, tc, test_loop_timer_precision, 30); add_tcase(s, tc, test_loop_timer_expire_leak, 30); add_tcase(s, tc, test_loop_timer_threads, 30); return s; } static Suite * loop_signal_suite(void) { TCase *tc; Suite *s = suite_create("loop_signal_suite"); add_tcase(s, tc, test_loop_sig_handling, 10); - add_tcase(s, tc, test_loop_sig_only_get_one); - add_tcase(s, tc, test_loop_sig_delete); - add_tcase(s, tc, test_loop_dont_override_other_signals); + add_tcase(s, tc, test_loop_sig_only_get_one, 0); + add_tcase(s, tc, test_loop_sig_delete, 0); + add_tcase(s, tc, test_loop_dont_override_other_signals, 0); return s; } int32_t main(void) { int32_t number_failed; SRunner *sr = srunner_create(loop_job_suite()); srunner_add_suite (sr, loop_timer_suite()); srunner_add_suite (sr, loop_signal_suite()); qb_log_init("check", LOG_USER, LOG_EMERG); atexit(qb_log_fini); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_INFO); qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/check_map.c b/tests/check_map.c index 5a98f75..9dfc21c 100644 --- a/tests/check_map.c +++ b/tests/check_map.c @@ -1,1045 +1,1045 @@ /* * Copyright (c) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * This file is part of libqb. * * libqb is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libqb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libqb. If not, see . */ #include "os_base.h" #include "check_common.h" #include #include #include const char *chars[] = { "0","1","2","3","4","5","6","7","8","9", "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z", "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z", NULL, }; const char *chars2[] = { "0","1","2","3","4","5","6","7","8","9", "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z", NULL, }; const char *composers[] = { "Béla Bartók", "Zoltán Kodály", "Ludwig van Beethoven", "Wolfgang Amadeus Mozart", "Leoš Janáček", "Benjamin Britten", "Josef Haydn", "Claude Debussy", "Charles Ives", /* Maybe in an alien language ... but they can cause trie crashes & conflicts */ "\x7e\x7f\x80\x81", "\x7e", "\x7e\x7f", "\x7e\x7f\x80", }; static char *notified_key = NULL; static void *notified_value = NULL; static void *notified_new_value = NULL; static void *notified_user_data = NULL; static int32_t notified_event = 0; static int32_t notified_event_prev = 0; static int32_t notified_events = 0; static void my_map_notification_iter(uint32_t event, char* key, void* old_value, void* value, void* user_data) { const char *p; void *data; qb_map_t *m = (qb_map_t *)user_data; qb_map_iter_t *it = qb_map_iter_create(m); notified_events++; for (p = qb_map_iter_next(it, &data); p; p = qb_map_iter_next(it, &data)) { printf("%s > %s\n", p, (char*) data); } qb_map_iter_free(it); } /* * create some entries * add a notifier * delete an entry * in the notifier iterate over the map. */ static void test_map_notifications_iter(qb_map_t *m) { int i; qb_map_put(m, "k1", "one"); qb_map_put(m, "k12", "two"); qb_map_put(m, "k34", "three"); ck_assert_int_eq(qb_map_count_get(m), 3); notified_events = 0; i = qb_map_notify_add(m, NULL, my_map_notification_iter, (QB_MAP_NOTIFY_DELETED | QB_MAP_NOTIFY_RECURSIVE), m); ck_assert_int_eq(i, 0); qb_map_rm(m, "k12"); ck_assert_int_eq(notified_events, 1); ck_assert_int_eq(qb_map_count_get(m), 2); } static void test_map_simple(qb_map_t *m, const char *name) { int i; const char *p; void *data; qb_map_iter_t *it; qb_map_put(m, "k1", "one"); qb_map_put(m, "k12", "two"); qb_map_put(m, "k34", "three"); ck_assert_int_eq(qb_map_count_get(m), 3); qb_map_put(m, "k3", "four"); ck_assert_int_eq(qb_map_count_get(m), 4); it = qb_map_iter_create(m); i = 0; for (p = qb_map_iter_next(it, &data); p; p = qb_map_iter_next(it, &data)) { printf("%25s(%d) %s > %s\n", name, i, p, (char*) data); i++; } qb_map_iter_free(it); ck_assert_int_eq(i, 4); ck_assert_str_eq(qb_map_get(m, "k34"), "three"); ck_assert_str_eq(qb_map_get(m, "k1"), "one"); ck_assert_str_eq(qb_map_get(m, "k12"), "two"); ck_assert_str_eq(qb_map_get(m, "k3"), "four"); qb_map_rm(m, "k12"); ck_assert_int_eq(qb_map_count_get(m), 3); qb_map_put(m, "9k", "nine"); qb_map_put(m, "k34", "not_three"); ck_assert_str_eq(qb_map_get(m, "k34"), "not_three"); ck_assert_int_eq(qb_map_count_get(m), 4); qb_map_destroy(m); } static int32_t my_traverse(const char *key, void *value, void *data) { ck_assert((*key) > 0); return QB_FALSE; } static int32_t check_order(const char *key, void *value, void *data) { int *o = (int*)data; ck_assert_str_eq(chars[*o], key); ck_assert_str_eq(chars[*o], value); (*o)++; return QB_FALSE; } static int32_t check_order2(const char *key, void *value, void *data) { int *o = (int*)data; ck_assert_str_eq(chars2[*o], key); ck_assert_str_eq(chars2[*o], value); (*o)++; return QB_FALSE; } static void test_map_search(qb_map_t* m) { int32_t i; int32_t removed; int order; char c[2]; const char *p; for (i = 0; chars[i]; i++) { qb_map_put(m, chars[i], chars[i]); } qb_map_foreach(m, my_traverse, NULL); ck_assert_int_eq(qb_map_count_get(m), (26*2 + 10)); order = 0; qb_map_foreach(m, check_order, &order); for (i = 0; i < 26; i++) { removed = qb_map_rm(m, chars[i + 10]); ck_assert(removed); } c[0] = '\0'; c[1] = '\0'; removed = qb_map_rm(m, c); ck_assert(!removed); qb_map_foreach(m, my_traverse, NULL); ck_assert_int_eq(qb_map_count_get(m), 26+10); order = 0; qb_map_foreach(m, check_order2, &order); for (i = 25; i >= 0; i--) { qb_map_put(m, chars[i + 10], chars[i + 10]); } order = 0; qb_map_foreach(m, check_order, &order); c[0] = '0'; p = qb_map_get(m, c); ck_assert(p && *p == *c); c[0] = 'A'; p = qb_map_get(m, c); ck_assert(p && *p == *c); c[0] = 'a'; p = qb_map_get(m, c); ck_assert(p && *p == *c); c[0] = 'z'; p = qb_map_get(m, c); ck_assert(p && *p == *c); c[0] = '!'; p = qb_map_get(m, c); ck_assert(p == NULL); c[0] = '='; p = qb_map_get(m, c); ck_assert(p == NULL); c[0] = '|'; p = qb_map_get(m, c); ck_assert(p == NULL); qb_map_destroy(m); } static void my_map_notification(uint32_t event, char* key, void* old_value, void* value, void* user_data) { notified_key = key; notified_value = old_value; notified_new_value = value; notified_user_data = user_data; notified_event_prev = notified_event; notified_event = event; } static void my_map_notification_2(uint32_t event, char* key, void* old_value, void* value, void* user_data) { } static void test_map_remove(qb_map_t *m) { const char * a, *b, *c, *d; int32_t i; int32_t removed; const char *remove_ch[] = {"o","m","k","j","i","g","f","e","d","b","a", NULL}; i = qb_map_notify_add(m, NULL, my_map_notification, (QB_MAP_NOTIFY_DELETED| QB_MAP_NOTIFY_REPLACED| QB_MAP_NOTIFY_RECURSIVE), m); ck_assert_int_eq(i, 0); for (i = 0; chars[i]; i++) { qb_map_put(m, chars[i], chars[i]); } a = "0"; qb_map_put(m, a, a); ck_assert(notified_key == chars[0]); ck_assert(notified_value == chars[0]); ck_assert(notified_user_data == m); notified_key = NULL; notified_value = NULL; notified_user_data = NULL; b = "5"; removed = qb_map_rm(m, b); ck_assert(removed); ck_assert(notified_key == chars[5]); ck_assert(notified_value == chars[5]); ck_assert(notified_user_data == m); notified_key = NULL; notified_value = NULL; notified_user_data = NULL; d = "1"; qb_map_put(m, d, d); ck_assert(notified_key == chars[1]); ck_assert(notified_value == chars[1]); ck_assert(notified_user_data == m); notified_key = NULL; notified_value = NULL; c = "2"; removed = qb_map_rm(m, c); ck_assert(removed); ck_assert(notified_key == chars[2]); ck_assert(notified_value == chars[2]); notified_key = NULL; notified_value = NULL; for (i = 0; remove_ch[i]; i++) { removed = qb_map_rm(m, remove_ch[i]); ck_assert(removed); } qb_map_destroy(m); } static void test_map_notifications_basic(qb_map_t *m) { int32_t i; /* with global notifier */ i = qb_map_notify_add(m, NULL, my_map_notification, (QB_MAP_NOTIFY_INSERTED| QB_MAP_NOTIFY_DELETED| QB_MAP_NOTIFY_REPLACED| QB_MAP_NOTIFY_RECURSIVE), m); ck_assert_int_eq(i, 0); notified_key = NULL; notified_value = NULL; notified_new_value = NULL; /* insert */ qb_map_put(m, "garden", "grow"); ck_assert_str_eq(notified_key, "garden"); ck_assert_str_eq(notified_new_value, "grow"); ck_assert(notified_user_data == m); /* update */ qb_map_put(m, "garden", "green"); ck_assert_str_eq(notified_key, "garden"); ck_assert_str_eq(notified_value, "grow"); ck_assert_str_eq(notified_new_value, "green"); ck_assert(notified_user_data == m); /* delete */ qb_map_rm(m, "garden"); ck_assert_str_eq(notified_key, "garden"); ck_assert_str_eq(notified_value, "green"); ck_assert(notified_user_data == m); /* no event with notifier removed */ i = qb_map_notify_del(m, NULL, my_map_notification, (QB_MAP_NOTIFY_INSERTED| QB_MAP_NOTIFY_DELETED| QB_MAP_NOTIFY_REPLACED| QB_MAP_NOTIFY_RECURSIVE)); ck_assert_int_eq(i, 0); notified_key = NULL; notified_value = NULL; notified_new_value = NULL; qb_map_put(m, "age", "67"); ck_assert(notified_key == NULL); ck_assert(notified_value == NULL); ck_assert(notified_new_value == NULL); /* deleting a non-existing notification */ i = qb_map_notify_del(m, "a", my_map_notification, (QB_MAP_NOTIFY_INSERTED| QB_MAP_NOTIFY_DELETED| QB_MAP_NOTIFY_REPLACED| QB_MAP_NOTIFY_RECURSIVE)); ck_assert_int_eq(i, -ENOENT); /* test uniquess */ qb_map_put(m, "fred", "null"); i = qb_map_notify_add(m, "fred", my_map_notification, QB_MAP_NOTIFY_REPLACED, m); ck_assert_int_eq(i, 0); i = qb_map_notify_add(m, "fred", my_map_notification, QB_MAP_NOTIFY_REPLACED, m); ck_assert_int_eq(i, -EEXIST); } /* test free'ing notifier * * input: * only one can be added * can only be added with NULL key (global) * output: * is the last notifier called (after deleted or replaced) * recursive is implicit */ static void test_map_notifications_free(qb_map_t *m) { int32_t i; i = qb_map_notify_add(m, "not global", my_map_notification, QB_MAP_NOTIFY_FREE, m); ck_assert_int_eq(i, -EINVAL); i = qb_map_notify_add(m, NULL, my_map_notification, QB_MAP_NOTIFY_FREE, m); ck_assert_int_eq(i, 0); i = qb_map_notify_add(m, NULL, my_map_notification_2, QB_MAP_NOTIFY_FREE, m); ck_assert_int_eq(i, -EEXIST); i = qb_map_notify_del_2(m, NULL, my_map_notification, QB_MAP_NOTIFY_FREE, m); ck_assert_int_eq(i, 0); i = qb_map_notify_add(m, NULL, my_map_notification, (QB_MAP_NOTIFY_FREE | QB_MAP_NOTIFY_REPLACED | QB_MAP_NOTIFY_DELETED | QB_MAP_NOTIFY_RECURSIVE), m); ck_assert_int_eq(i, 0); qb_map_put(m, "garden", "grow"); /* update */ qb_map_put(m, "garden", "green"); ck_assert_int_eq(notified_event_prev, QB_MAP_NOTIFY_REPLACED); ck_assert_int_eq(notified_event, QB_MAP_NOTIFY_FREE); /* delete */ qb_map_rm(m, "garden"); ck_assert_int_eq(notified_event_prev, QB_MAP_NOTIFY_DELETED); ck_assert_int_eq(notified_event, QB_MAP_NOTIFY_FREE); } static void test_map_notifications_prefix(qb_map_t *m) { int32_t i; /* with prefix notifier */ i = qb_map_notify_add(m, "add", my_map_notification, (QB_MAP_NOTIFY_INSERTED| QB_MAP_NOTIFY_DELETED| QB_MAP_NOTIFY_REPLACED| QB_MAP_NOTIFY_RECURSIVE), &i); ck_assert_int_eq(i, 0); /* insert */ qb_map_put(m, "adder", "snake"); ck_assert_str_eq(notified_key, "adder"); ck_assert_str_eq(notified_new_value, "snake"); ck_assert(notified_user_data == &i); /* insert (no match) */ notified_key = NULL; notified_value = NULL; notified_new_value = NULL; qb_map_put(m, "adjust", "it"); ck_assert(notified_key == NULL); ck_assert(notified_value == NULL); ck_assert(notified_new_value == NULL); /* update */ qb_map_put(m, "adder", "+++"); ck_assert_str_eq(notified_key, "adder"); ck_assert_str_eq(notified_value, "snake"); ck_assert_str_eq(notified_new_value, "+++"); /* delete */ qb_map_rm(m, "adder"); ck_assert_str_eq(notified_key, "adder"); ck_assert_str_eq(notified_value, "+++"); } static void test_map_traverse_ordered(qb_map_t *m) { int32_t i; const char *p; char *result; void *data; qb_map_iter_t *it = qb_map_iter_create(m); for (i = 0; chars[i]; i++) { qb_map_put(m, chars[i], chars[i]); } result = calloc(sizeof(char), 26 * 2 + 10 + 1); i = 0; for (p = qb_map_iter_next(it, &data); p; p = qb_map_iter_next(it, &data)) { result[i] = *(char*) data; i++; } qb_map_iter_free(it); ck_assert_str_eq(result, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); qb_map_destroy(m); } static int32_t traverse_and_remove_func(const char *key, void *value, void *data) { int kk = random() % 30; qb_map_t *m = (qb_map_t *)data; qb_map_rm(m, chars[kk]); qb_map_put(m, chars[kk+30], key); return QB_FALSE; } static void test_map_iter_safety(qb_map_t *m, int32_t ordered) { void *data; void *data2; const char *p; const char *p2; qb_map_iter_t *it; qb_map_iter_t *it2; int32_t found_good = QB_FALSE; qb_map_put(m, "aaaa", "aye"); qb_map_put(m, "bbbb", "bee"); qb_map_put(m, "cccc", "sea"); it = qb_map_iter_create(m); it2 = qb_map_iter_create(m); while ((p = qb_map_iter_next(it, &data)) != NULL) { printf("1: %s == %s\n", p, (char*)data); if (strcmp(p, "bbbb") == 0) { qb_map_rm(m, "bbbb"); qb_map_rm(m, "cccc"); qb_map_rm(m, "aaaa"); qb_map_put(m, "fffff", "yum"); while ((p2 = qb_map_iter_next(it2, &data2)) != NULL) { printf("2: %s == %s\n", p2, (char*)data2); if (strcmp(p2, "fffff") == 0) { qb_map_put(m, "ggggg", "good"); } } qb_map_iter_free(it2); } if (strcmp(p, "ggggg") == 0) { found_good = QB_TRUE; } } qb_map_iter_free(it); if (ordered) { ck_assert_int_eq(found_good, QB_TRUE); } qb_map_destroy(m); } static void test_map_iter_prefix(qb_map_t *m) { void *data; const char *p; qb_map_iter_t *it; int count; qb_map_put(m, "aaaa", "aye"); qb_map_put(m, "facc", "nope"); qb_map_put(m, "abbb", "bee"); qb_map_put(m, "a.ac", "nope"); qb_map_put(m, "aacc", "yip"); qb_map_put(m, "cacc", "nope"); qb_map_put(m, "c", "----"); count = 0; it = qb_map_pref_iter_create(m, "aa"); while ((p = qb_map_iter_next(it, &data)) != NULL) { printf("1: %s == %s\n", p, (char*)data); count++; } qb_map_iter_free(it); ck_assert_int_eq(count, 2); count = 0; it = qb_map_pref_iter_create(m, "a"); while ((p = qb_map_iter_next(it, &data)) != NULL) { printf("2: %s == %s\n", p, (char*)data); count++; } qb_map_iter_free(it); ck_assert_int_eq(count, 4); count = 0; it = qb_map_pref_iter_create(m, "zz"); while ((p = qb_map_iter_next(it, &data)) != NULL) { printf("??: %s == %s\n", p, (char*)data); count++; } qb_map_iter_free(it); ck_assert_int_eq(count, 0); count = 0; it = qb_map_pref_iter_create(m, "c"); while ((p = qb_map_iter_next(it, &data)) != NULL) { printf("3: %s == %s\n", p, (char*)data); count++; } qb_map_iter_free(it); ck_assert_int_eq(count, 2); qb_map_destroy(m); } static void test_map_traverse_unordered(qb_map_t *m) { int32_t i; srand(time(NULL)); for (i = 0; i < 30; i++) { qb_map_put(m, chars[i], chars[i]); } qb_map_foreach(m, traverse_and_remove_func, m); qb_map_destroy(m); } static int32_t my_counter_traverse(const char *key, void *value, void *data) { int32_t *c = (int32_t*)data; (*c)++; return QB_FALSE; } static void test_map_load(qb_map_t *m, const char* test_name) { char word[1000]; char *w; FILE *fp; int32_t res = 0; int32_t count; int32_t count2; float ops; float secs; void *value; qb_util_stopwatch_t *sw; ck_assert(m != NULL); sw = qb_util_stopwatch_create(); #define MAX_WORDS 100000 /* * Load with dictionary */ fp = fopen("/usr/share/dict/words", "r"); qb_util_stopwatch_start(sw); count = 0; while (fgets(word, sizeof(word), fp) && count < MAX_WORDS) { w = strdup(word); qb_map_put(m, w, w); count++; } qb_util_stopwatch_stop(sw); ck_assert_int_eq(qb_map_count_get(m), count); fclose(fp); secs = qb_util_stopwatch_sec_elapsed_get(sw); ops = (float)count / secs; qb_log(LOG_INFO, "%25s %12.2f puts/sec (%d/%fs)\n", test_name, ops, count, secs); /* * Verify dictionary produces correct values */ fp = fopen("/usr/share/dict/words", "r"); qb_util_stopwatch_start(sw); count2 = 0; while (fgets(word, sizeof(word), fp) && count2 < MAX_WORDS) { value = qb_map_get(m, word); ck_assert_str_eq(word, value); count2++; } qb_util_stopwatch_stop(sw); fclose(fp); secs = qb_util_stopwatch_sec_elapsed_get(sw); ops = (float)count2 / secs; qb_log(LOG_INFO, "%25s %12.2f gets/sec (%d/%fs)\n", test_name, ops, count2, secs); /* * time the iteration */ count2 = 0; qb_util_stopwatch_start(sw); qb_map_foreach(m, my_counter_traverse, &count2); qb_util_stopwatch_stop(sw); ck_assert_int_eq(qb_map_count_get(m), count2); secs = qb_util_stopwatch_sec_elapsed_get(sw); ops = (float)count2 / secs; qb_log(LOG_INFO, "%25s %12.2f iters/sec (%d/%fs)\n", test_name, ops, count2, secs); /* * Delete all dictionary entries */ fp = fopen("/usr/share/dict/words", "r"); qb_util_stopwatch_start(sw); count2 = 0; while (fgets(word, sizeof(word), fp) && count2 < MAX_WORDS) { res = qb_map_rm(m, word); ck_assert_int_eq(res, QB_TRUE); count2++; } qb_util_stopwatch_stop(sw); ck_assert_int_eq(qb_map_count_get(m), 0); fclose(fp); secs = qb_util_stopwatch_sec_elapsed_get(sw); ops = (float)count2 / secs; qb_log(LOG_INFO, "%25s %12.2f dels/sec (%d/%fs)\n", test_name, ops, count2, secs); } static void test_accents_load(qb_map_t *m, const char* test_name) { int i; int32_t res = 0; int32_t count = 0; int32_t count2; void *value; ck_assert(m != NULL); /* * Load accented names */ for (i=0; i * * This file is part of libqb. * * libqb is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libqb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libqb. If not, see . */ #include #include #include #include #include "check_common.h" #include #include #include #include START_TEST(test_ring_buffer1) { char my_buf[512]; struct qb_ipc_request_header hdr; char *str; qb_ringbuffer_t *rb; int32_t i; int32_t b; ssize_t actual; ssize_t avail; rb = qb_rb_open("test1", 200, QB_RB_FLAG_CREATE, 0); ck_assert(rb != NULL); for (b = 0; b < 3; b++) { memcpy(&hdr, my_buf, sizeof(struct qb_ipc_request_header)); str = my_buf + sizeof(struct qb_ipc_request_header); for (i = 0; i < 900; i++) { hdr.id = __LINE__ + i; hdr.size = sprintf(str, "ID: %d (%s + i(%d)) -- %s-%s!", hdr.id, "actually the line number", i, __func__, __FILE__) + 1; hdr.size += sizeof(struct qb_ipc_request_header); memcpy(my_buf, &hdr, sizeof(struct qb_ipc_request_header)); avail = qb_rb_space_free(rb); actual = qb_rb_chunk_write(rb, my_buf, hdr.size); if (avail < (hdr.size + (3 * sizeof(uint32_t)))) { ck_assert_int_eq(actual, -EAGAIN); } else { ck_assert_int_eq(actual, hdr.size); } } memset(my_buf, 0, sizeof(my_buf)); memcpy(&hdr, my_buf, sizeof(struct qb_ipc_request_header)); str = my_buf + sizeof(struct qb_ipc_request_header); for (i = 0; i < 15; i++) { actual = qb_rb_chunk_read(rb, my_buf, 512, 0); if (actual < 0) { ck_assert_int_eq(0, qb_rb_chunks_used(rb)); break; } memcpy(&hdr, my_buf, sizeof(struct qb_ipc_request_header)); str[actual - sizeof(struct qb_ipc_request_header)] = '\0'; ck_assert_int_eq(actual, hdr.size); } } qb_rb_close(rb); } END_TEST /* * nice size (int64) */ START_TEST(test_ring_buffer2) { qb_ringbuffer_t *t; int32_t i; int64_t v = 7891034; int64_t *new_data; ssize_t l; t = qb_rb_open("test2", 200 * sizeof(int64_t), QB_RB_FLAG_CREATE, 0); ck_assert(t != NULL); for (i = 0; i < 200; i++) { l = qb_rb_chunk_write(t, &v, sizeof(v)); ck_assert_int_eq(l, sizeof(v)); } for (i = 0; i < 100; i++) { l = qb_rb_chunk_peek(t, (void **)&new_data, 0); ck_assert_int_eq(l, sizeof(v)); ck_assert(v == *new_data); qb_rb_chunk_reclaim(t); } for (i = 0; i < 100; i++) { l = qb_rb_chunk_write(t, &v, sizeof(v)); ck_assert_int_eq(l, sizeof(v)); } for (i = 0; i < 100; i++) { l = qb_rb_chunk_peek(t, (void **)&new_data, 0); if (l == 0) { /* no more to read */ break; } ck_assert_int_eq(l, sizeof(v)); ck_assert(v == *new_data); qb_rb_chunk_reclaim(t); } qb_rb_close(t); } END_TEST /* * odd size (10) */ START_TEST(test_ring_buffer3) { qb_ringbuffer_t *t; int32_t i; char v[] = "1234567891"; char out[32]; ssize_t l; size_t len = strlen(v) + 1; t = qb_rb_open("test3", 10, QB_RB_FLAG_CREATE | QB_RB_FLAG_OVERWRITE, 0); ck_assert(t != NULL); for (i = 0; i < 9000; i++) { l = qb_rb_chunk_write(t, v, len); ck_assert_int_eq(l, len); } for (i = 0; i < 2000; i++) { l = qb_rb_chunk_read(t, (void *)out, 32, 0); if (l < 0) { /* no more to read */ break; } ck_assert_int_eq(l, len); ck_assert_str_eq(v, out); } qb_rb_close(t); } END_TEST START_TEST(test_ring_buffer4) { qb_ringbuffer_t *t; char data[] = "1234567891"; int32_t i; char *new_data; ssize_t l; t = qb_rb_open("test4", 10, QB_RB_FLAG_CREATE | QB_RB_FLAG_OVERWRITE, 0); ck_assert(t != NULL); for (i = 0; i < 2000; i++) { l = qb_rb_chunk_write(t, data, strlen(data)); ck_assert_int_eq(l, strlen(data)); if (i == 0) { data[0] = 'b'; } } for (i = 0; i < 2000; i++) { l = qb_rb_chunk_peek(t, (void **)&new_data, 0); if (l < 0) { /* no more to read */ break; } ck_assert_int_eq(l, strlen(data)); qb_rb_chunk_reclaim(t); } qb_rb_close(t); } END_TEST static Suite *rb_suite(void) { TCase *tc; Suite *s = suite_create("ringbuffer"); - add_tcase(s, tc, test_ring_buffer1); - add_tcase(s, tc, test_ring_buffer2); - add_tcase(s, tc, test_ring_buffer3); - add_tcase(s, tc, test_ring_buffer4); + add_tcase(s, tc, test_ring_buffer1, 0); + add_tcase(s, tc, test_ring_buffer2, 0); + add_tcase(s, tc, test_ring_buffer3, 0); + add_tcase(s, tc, test_ring_buffer4, 0); return s; } int32_t main(void) { int32_t number_failed; Suite *s = rb_suite(); SRunner *sr = srunner_create(s); qb_log_init("check", LOG_USER, LOG_EMERG); atexit(qb_log_fini); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_INFO); qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/check_tlist.c b/tests/check_tlist.c index 1fecec0..c1b963f 100644 --- a/tests/check_tlist.c +++ b/tests/check_tlist.c @@ -1,294 +1,294 @@ /* * Copyright (c) 2021 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse * * This file is part of libqb. * * libqb is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libqb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libqb. If not, see . */ #include "os_base.h" #include "check_common.h" #include "tlist.h" #include #include #include #include #define SHORT_TIMEOUT (100 * QB_TIME_NS_IN_MSEC) #define LONG_TIMEOUT (60 * QB_TIME_NS_IN_SEC) #define SPEED_TEST_NO_ITEMS 10000 #define HEAP_TEST_NO_ITEMS 20 /* * Valid heap checking is slow */ #define HEAP_SPEED_TEST_NO_ITEMS 1000 static int timer_list_fn1_called = 0; static void timer_list_fn1(void *data) { ck_assert(data == &timer_list_fn1_called); timer_list_fn1_called++; } static void sleep_ns(long long int ns) { (void)poll(NULL, 0, (ns / QB_TIME_NS_IN_MSEC)); } START_TEST(test_check_basic) { struct timerlist tlist; timer_handle thandle; int res; uint64_t u64; timerlist_init(&tlist); /* * Check adding short duration and calling callback */ res = timerlist_add_duration(&tlist, timer_list_fn1, &timer_list_fn1_called, SHORT_TIMEOUT / 2, &thandle); ck_assert_int_eq(res, 0); sleep_ns(SHORT_TIMEOUT); u64 = timerlist_msec_duration_to_expire(&tlist); ck_assert(u64 == 0); timer_list_fn1_called = 0; timerlist_expire(&tlist); ck_assert_int_eq(timer_list_fn1_called, 1); u64 = timerlist_msec_duration_to_expire(&tlist); ck_assert(u64 == -1); /* * Check callback is not called (long timeout) */ res = timerlist_add_duration(&tlist, timer_list_fn1, &timer_list_fn1_called, LONG_TIMEOUT / 2, &thandle); ck_assert_int_eq(res, 0); sleep_ns(SHORT_TIMEOUT); u64 = timerlist_msec_duration_to_expire(&tlist); ck_assert(u64 > 0); timer_list_fn1_called = 0; timerlist_expire(&tlist); ck_assert_int_eq(timer_list_fn1_called, 0); u64 = timerlist_msec_duration_to_expire(&tlist); ck_assert(u64 > 0); /* * Delete timer */ timerlist_del(&tlist, thandle); u64 = timerlist_msec_duration_to_expire(&tlist); ck_assert(u64 == -1); timerlist_destroy(&tlist); } END_TEST START_TEST(test_check_speed) { struct timerlist tlist; timer_handle thandle[SPEED_TEST_NO_ITEMS]; int res; uint64_t u64; int i; timerlist_init(&tlist); /* * Check adding a lot of short duration and deleting */ for (i = 0; i < SPEED_TEST_NO_ITEMS; i++) { res = timerlist_add_duration(&tlist, timer_list_fn1, &timer_list_fn1_called, SHORT_TIMEOUT / 2, &thandle[i]); ck_assert_int_eq(res, 0); } for (i = 0; i < SPEED_TEST_NO_ITEMS; i++) { timerlist_del(&tlist, thandle[i]); } u64 = timerlist_msec_duration_to_expire(&tlist); ck_assert(u64 == -1); /* * Check adding a lot of short duration and calling callback */ for (i = 0; i < SPEED_TEST_NO_ITEMS; i++) { res = timerlist_add_duration(&tlist, timer_list_fn1, &timer_list_fn1_called, SHORT_TIMEOUT / 2, &thandle[i]); ck_assert_int_eq(res, 0); } u64 = timerlist_msec_duration_to_expire(&tlist); ck_assert(u64 != -1); sleep_ns(SHORT_TIMEOUT); timer_list_fn1_called = 0; timerlist_expire(&tlist); ck_assert_int_eq(timer_list_fn1_called, SPEED_TEST_NO_ITEMS); u64 = timerlist_msec_duration_to_expire(&tlist); ck_assert(u64 == -1); timerlist_destroy(&tlist); } END_TEST START_TEST(test_check_heap) { struct timerlist tlist; int i; timer_handle tlist_entry[HEAP_TEST_NO_ITEMS]; timer_handle tlist_speed_entry[HEAP_SPEED_TEST_NO_ITEMS]; int res; timerlist_init(&tlist); /* * Empty tlist */ ck_assert(timerlist_msec_duration_to_expire(&tlist) == -1); /* * Add items in standard and reverse order */ for (i = 0; i < HEAP_TEST_NO_ITEMS / 2; i++) { res = timerlist_add_duration(&tlist, timer_list_fn1, &timer_list_fn1_called, LONG_TIMEOUT * ((HEAP_TEST_NO_ITEMS - i) + 1), &tlist_entry[i * 2]); ck_assert_int_eq(res, 0); res = timerlist_add_duration(&tlist, timer_list_fn1, &timer_list_fn1_called, LONG_TIMEOUT * (i + 1), &tlist_entry[i * 2 + 1]); ck_assert_int_eq(res, 0); ck_assert(timerlist_debug_is_valid_heap(&tlist)); } /* * Remove items */ for (i = 0; i < HEAP_TEST_NO_ITEMS; i++) { timerlist_del(&tlist, tlist_entry[i]); ck_assert(timerlist_debug_is_valid_heap(&tlist)); } ck_assert(timerlist_msec_duration_to_expire(&tlist) == -1); /* * Add items again in increasing order */ for (i = 0; i < HEAP_TEST_NO_ITEMS; i++) { res = timerlist_add_duration(&tlist, timer_list_fn1, &timer_list_fn1_called, LONG_TIMEOUT * (i + 1), &tlist_entry[i]); ck_assert_int_eq(res, 0); ck_assert(timerlist_debug_is_valid_heap(&tlist)); } /* * Try delete every third item and test if heap property is kept */ i = 0; while (tlist.size > 0) { i = (i + 3) % HEAP_TEST_NO_ITEMS; while (tlist_entry[i] == NULL) { i = (i + 1) % HEAP_TEST_NO_ITEMS; } timerlist_del(&tlist, tlist_entry[i]); tlist_entry[i] = NULL; ck_assert(timerlist_debug_is_valid_heap(&tlist)); } ck_assert(timerlist_msec_duration_to_expire(&tlist) == -1); /* * Speed test */ for (i = 0; i < HEAP_SPEED_TEST_NO_ITEMS; i++) { res = timerlist_add_duration(&tlist, timer_list_fn1, &timer_list_fn1_called, SHORT_TIMEOUT / 2, &tlist_speed_entry[i]); ck_assert_int_eq(res, 0); ck_assert(timerlist_debug_is_valid_heap(&tlist)); } for (i = 0; i < HEAP_SPEED_TEST_NO_ITEMS; i++) { timerlist_del(&tlist, tlist_speed_entry[i]); ck_assert(timerlist_debug_is_valid_heap(&tlist)); } /* * Free list */ timerlist_destroy(&tlist); } END_TEST static Suite *tlist_suite(void) { TCase *tc; Suite *s = suite_create("tlist"); - add_tcase(s, tc, test_check_basic); + add_tcase(s, tc, test_check_basic, 0); add_tcase(s, tc, test_check_speed, 30); add_tcase(s, tc, test_check_heap, 30); return s; } int32_t main(void) { int32_t number_failed; Suite *s = tlist_suite(); SRunner *sr = srunner_create(s); qb_log_init("check", LOG_USER, LOG_EMERG); atexit(qb_log_fini); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_INFO); qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/check_util.c b/tests/check_util.c index 0407b97..e5ab635 100644 --- a/tests/check_util.c +++ b/tests/check_util.c @@ -1,187 +1,187 @@ /* * Copyright (c) 2010 Red Hat, Inc. * * All rights reserved. * * Author: Steven Dake * * This file is part of libqb. * * libqb is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libqb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libqb. If not, see . */ #include "os_base.h" #include "check_common.h" #include #include #include #define assert_int_between(_c, _lower, _upper) \ _ck_assert_int(_c, >=, _lower); \ _ck_assert_int(_c, <=, _upper); START_TEST(test_check_overwrite) { uint64_t res; uint32_t last; qb_util_stopwatch_t *sw = qb_util_stopwatch_create(); qb_util_stopwatch_split_ctl(sw, 5, QB_UTIL_SW_OVERWRITE); res = qb_util_stopwatch_split(sw); assert_int_between(res, 0, 100); usleep(10000); res = qb_util_stopwatch_split(sw); assert_int_between(res, 9000, 11000); usleep(20000); res = qb_util_stopwatch_split(sw); assert_int_between(res, 19000, 21000); usleep(30000); res = qb_util_stopwatch_split(sw); assert_int_between(res, 29000, 31000); usleep(40000); res = qb_util_stopwatch_split(sw); assert_int_between(res, 39000, 41000); /* * window should be 100000 (40000 + 30000 + 20000 + 10000) usec */ last = qb_util_stopwatch_split_last(sw); res = qb_util_stopwatch_time_split_get(sw, last, last - 4); assert_int_between(res, 95000, 105000); usleep(50000); res = qb_util_stopwatch_split(sw); assert_int_between(res, 49000, 52000); /* * window should be 140000 (50000 + 40000 + 30000 + 20000) usec */ last = qb_util_stopwatch_split_last(sw); res = qb_util_stopwatch_time_split_get(sw, last, last - 4); assert_int_between(res, 135000, 145000); usleep(25000); qb_util_stopwatch_split(sw); /* ask for a split that has been overwritten. */ res = qb_util_stopwatch_time_split_get(sw, last, 1); ck_assert_int_eq(res, 0); /* iterating */ last = qb_util_stopwatch_split_last(sw); do { res = qb_util_stopwatch_time_split_get(sw, last, last); qb_log(LOG_INFO, "overwrite split %d is %"PRIu64"", last, res); last--; } while (res > 0); qb_util_stopwatch_free(sw); } END_TEST START_TEST(test_check_normal) { uint64_t res; uint32_t last; qb_util_stopwatch_t *sw = qb_util_stopwatch_create(); qb_util_stopwatch_split_ctl(sw, 3, 0); qb_util_stopwatch_start(sw); usleep(33000); /* 1 */ res = qb_util_stopwatch_split(sw); assert_int_between(res, 30000, 36000); last = qb_util_stopwatch_split_last(sw); ck_assert_int_eq(last, 0); usleep(10000); /* 2 */ res = qb_util_stopwatch_split(sw); assert_int_between(res, 9000, 11000); usleep(20000); /* 3 */ res = qb_util_stopwatch_split(sw); assert_int_between(res, 19000, 21000); /* no more space */ res = qb_util_stopwatch_split(sw); ck_assert_int_eq(res, 0); /* * split should be 30000 (10000 + 20000) usec */ last = qb_util_stopwatch_split_last(sw); ck_assert_int_eq(last, 2); res = qb_util_stopwatch_time_split_get(sw, last, 0); assert_int_between(res, 25000, 35000); /* ask for a split that has beyond the max. */ res = qb_util_stopwatch_time_split_get(sw, 3, 2); ck_assert_int_eq(res, 0); /* iterating */ last = qb_util_stopwatch_split_last(sw); do { res = qb_util_stopwatch_time_split_get(sw, last, last); qb_log(LOG_INFO, "normal split %d is %"PRIu64"", last, res); last--; } while (res > 0); qb_util_stopwatch_free(sw); } END_TEST static Suite *util_suite(void) { TCase *tc; Suite *s = suite_create("qb_util"); - add_tcase(s, tc, test_check_overwrite); - add_tcase(s, tc, test_check_normal); + add_tcase(s, tc, test_check_overwrite, 0); + add_tcase(s, tc, test_check_normal, 0); return s; } int32_t main(void) { int32_t number_failed; Suite *s = util_suite(); SRunner *sr = srunner_create(s); qb_log_init("check", LOG_USER, LOG_EMERG); atexit(qb_log_fini); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_INFO); qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; }