diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 667df663f..47a420826 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(core ${PROJECT_BINARY_DIR}/core) +add_subdirectory(hotkey ${PROJECT_BINARY_DIR}/hotkey) add_subdirectory(protocol ${PROJECT_BINARY_DIR}/protocol) add_subdirectory(storage ${PROJECT_BINARY_DIR}/storage) add_subdirectory(time ${PROJECT_BINARY_DIR}/time) diff --git a/src/hotkey/CMakeLists.txt b/src/hotkey/CMakeLists.txt new file mode 100644 index 000000000..cd137ef01 --- /dev/null +++ b/src/hotkey/CMakeLists.txt @@ -0,0 +1,6 @@ +set(SOURCE + hotkey.c + kc_map.c + key_window.c) + +add_library(hotkey ${SOURCE}) diff --git a/src/hotkey/constant.h b/src/hotkey/constant.h new file mode 100644 index 000000000..1cde643f0 --- /dev/null +++ b/src/hotkey/constant.h @@ -0,0 +1,3 @@ +#pragma once + +#define MAX_KEY_LEN 250 diff --git a/src/hotkey/hotkey.c b/src/hotkey/hotkey.c new file mode 100644 index 000000000..b14ae51c4 --- /dev/null +++ b/src/hotkey/hotkey.c @@ -0,0 +1,83 @@ +#include "hotkey.h" + +#include "constant.h" +#include "kc_map.h" +#include "key_window.h" + +#include +#include + +#define HOTKEY_MODULE_NAME "hotkey::hotkey" + +bool hotkey_enabled = false; + +static uint64_t hotkey_counter; + +static bool hotkey_init = false; +static uint32_t hotkey_window_size = HOTKEY_WINDOW_SIZE; +static uint32_t hotkey_rate = HOTKEY_RATE; +static uint32_t hotkey_threshold = HOTKEY_THRESHOLD; +static uint32_t hotkey_window_size_cur = 0; + +void +hotkey_setup(hotkey_options_st *options) +{ + log_info("Set up the %s module", HOTKEY_MODULE_NAME); + + if (options != NULL) { + hotkey_enabled = option_bool(&options->hotkey_enable); + hotkey_window_size = option_uint(&options->hotkey_sample_size); + hotkey_rate = option_uint(&options->hotkey_sample_rate); + hotkey_threshold = (uint32_t)(option_fpn(&options->hotkey_threshold_ratio) * hotkey_window_size); + } + + hotkey_window_size_cur = 0; + hotkey_counter = 0; + key_window_setup(hotkey_window_size); + /* TODO: determine whether table size should be a tuneable parameter */ + kc_map_setup(hotkey_window_size, hotkey_window_size); + hotkey_init = true; +} + +void +hotkey_teardown(void) +{ + log_info("Tear down the %s module", HOTKEY_MODULE_NAME); + + if (!hotkey_init) { + log_warn("%s was not setup", HOTKEY_MODULE_NAME); + return; + } + + hotkey_enabled = false; + key_window_teardown(); + kc_map_teardown(); + hotkey_init = false; +} + +bool +hotkey_sample(const struct bstring *key) +{ + if (++hotkey_counter % hotkey_rate == 0) { + /* sample this key */ + uint32_t freq; + + if (key_window_len() == hotkey_window_size) { + char buf[MAX_KEY_LEN]; + struct bstring popped; + + popped.data = buf; + + /* pop from key_window, decrement in counter table */ + popped.len = key_window_pop(popped.data); + kc_map_decr(&popped); + } + + key_window_push(key); + freq = kc_map_incr(key); + + return freq >= hotkey_threshold; + } + + return false; +} diff --git a/src/hotkey/hotkey.h b/src/hotkey/hotkey.h new file mode 100644 index 000000000..3b6350c76 --- /dev/null +++ b/src/hotkey/hotkey.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include +#include + +/* TODO(kevyang): add stats for hotkey module */ + +#define HOTKEY_WINDOW_SIZE 10000 /* keep last 10000 keys sampled by default */ +#define HOTKEY_RATE 100 /* sample one in every 100 keys by default */ +#define HOTKEY_THRESHOLD_RATIO 0.01 /* signal hotkey if key takes up >= 0.01 of all keys by default */ +#define HOTKEY_THRESHOLD (uint32_t)(HOTKEY_THRESHOLD_RATIO * HOTKEY_WINDOW_SIZE) + +/* name type default description */ +#define HOTKEY_OPTION(ACTION) \ + ACTION( hotkey_enable, OPTION_TYPE_BOOL, false, "use hotkey detection?" )\ + ACTION( hotkey_sample_size, OPTION_TYPE_UINT, HOTKEY_WINDOW_SIZE, "number of keys to maintain" )\ + ACTION( hotkey_sample_rate, OPTION_TYPE_UINT, HOTKEY_RATE, "hotkey sample ratio" )\ + ACTION( hotkey_threshold_ratio, OPTION_TYPE_UINT, HOTKEY_THRESHOLD_RATIO, "threshold for hotkey signal") + +typedef struct { + HOTKEY_OPTION(OPTION_DECLARE) +} hotkey_options_st; + +extern bool hotkey_enabled; + +void hotkey_setup(hotkey_options_st *options); +void hotkey_teardown(void); +bool hotkey_sample(const struct bstring *key); diff --git a/src/hotkey/kc_map.c b/src/hotkey/kc_map.c new file mode 100644 index 000000000..b4b4d6356 --- /dev/null +++ b/src/hotkey/kc_map.c @@ -0,0 +1,244 @@ +#include "kc_map.h" + +#include "constant.h" + +#include +#include +#include +#include +#include + +#define KC_MAP_MODULE_NAME "hotkey::kc_map" + +struct kc_map_entry { + STAILQ_ENTRY(kc_map_entry) next; /* entry in hash table or pool */ + + char key[MAX_KEY_LEN]; + uint32_t klen; + uint32_t count; +}; + +STAILQ_HEAD(kcme_slh, kc_map_entry); + +static struct kcme_slh *table = NULL; +static uint32_t table_size = 0; /* number of buckets in table */ +static bool kc_map_init = false; + +FREEPOOL(kcme_pool, kcmeq, kc_map_entry); +static struct kcme_pool kcmep; +static bool kcmep_init = false; + +static void +kc_map_entry_reset(struct kc_map_entry *kcme) +{ + kcme->klen = 0; + kcme->count = 0; +} + +static struct kc_map_entry * +kc_map_entry_create(void) +{ + struct kc_map_entry *kcme = cc_alloc(sizeof(*kcme)); + + if (kcme == NULL) { + return NULL; + } + + kc_map_entry_reset(kcme); + + return kcme; +} + +static void +kc_map_entry_destroy(struct kc_map_entry **kc_map_entry) +{ + struct kc_map_entry *kcme = *kc_map_entry; + ASSERT(kcme != NULL); + + cc_free(kcme); + *kc_map_entry = NULL; +} + +static void +kc_map_entry_pool_destroy(void) +{ + struct kc_map_entry *kcme, *tkcme; + + if (!kcmep_init) { + log_warn("kc_map_entry pool was not created, ignore"); + return; + } + + log_info("destroying kc_map_entry pool: free %"PRIu32, kcmep.nfree); + + FREEPOOL_DESTROY(kcme, tkcme, &kcmep, next, kc_map_entry_destroy); + kcmep_init = false; +} + +static struct kc_map_entry * +kc_map_entry_borrow(void) +{ + struct kc_map_entry *kcme; + + FREEPOOL_BORROW(kcme, &kcmep, next, kc_map_entry_create); + if (kcme == NULL) { + log_debug("borrow kc_map_entry failed: OOM"); + return NULL; + } + kc_map_entry_reset(kcme); + + return kcme; +} + +static void +kc_map_entry_return(struct kc_map_entry **kc_map_entry) +{ + struct kc_map_entry *kcme = *kc_map_entry; + + if (kcme == NULL) { + return; + } + + FREEPOOL_RETURN(kcme, &kcmep, next); + + *kc_map_entry = NULL; +} + +static void +kc_map_entry_pool_create(uint32_t max) +{ + struct kc_map_entry *kcme; + + if (kcmep_init) { + log_warn("kc_map_entry pool has already been created, re-creating"); + kc_map_entry_pool_destroy(); + } + + log_info("creating kc_map_entry pool: max %"PRIu32, max); + + FREEPOOL_CREATE(&kcmep, max); + kcmep_init = true; + + FREEPOOL_PREALLOC(kcme, &kcmep, max, next, kc_map_entry_create); + if (kcmep.nfree < max) { + log_crit("cannot preallocate kc_map_entry pool, OOM. abort"); + exit(EXIT_FAILURE); + } +} + +void +kc_map_setup(uint32_t size, uint32_t poolsize) +{ + uint32_t i; + + log_info("Set up the %s module", KC_MAP_MODULE_NAME); + + if (kc_map_init) { + log_warn("%s has already been setup, ignore", KC_MAP_MODULE_NAME); + return; + } + + table = cc_alloc(sizeof(*table) * size); + table_size = size; + + if (table == NULL) { + log_crit("Could not allocate counter table for hotkey - OOM"); + exit(EXIT_FAILURE); + } + + for (i = 0; i < size; ++i) { + STAILQ_INIT(&table[i]); + } + + kc_map_entry_pool_create(poolsize); +} + +void +kc_map_teardown(void) +{ + log_info("Tear down the %s module", KC_MAP_MODULE_NAME); + + if (!kc_map_init) { + log_warn("%s was not setup", KC_MAP_MODULE_NAME); + } + + if (table != NULL) { + uint32_t i; + /* free all entries in table */ + for (i = 0; i < table_size; ++i) { + struct kc_map_entry *kcme, *tkcme; + STAILQ_FOREACH_SAFE(kcme, &(table[i]), next, tkcme) { + kc_map_entry_return(&kcme); + } + } + } + + kc_map_entry_pool_destroy(); + kc_map_init = false; +} + +static inline struct kcme_slh * +_get_bucket(const struct bstring *key) +{ + return &(table[hash(key->data, key->len, 0) % table_size]); +} + +uint32_t +kc_map_incr(const struct bstring *key) +{ + struct kc_map_entry *kcme; + struct kcme_slh *bucket; + + ASSERT(key->len <= MAX_KEY_LEN); + + bucket = _get_bucket(key); + + /* iterate through bucket looking for item */ + for (kcme = STAILQ_FIRST(bucket); kcme != NULL; kcme = STAILQ_NEXT(kcme, next)) { + if ((key->len == kcme->klen) && cc_memcmp(key->data, kcme->key, key->len) == 0) { + /* found item */ + return ++kcme->count; + } + } + + /* not found, insert entry */ + kcme = kc_map_entry_borrow(); + cc_memcpy(kcme->key, key->data, key->len); + kcme->klen = key->len; + kcme->count = 1; + STAILQ_INSERT_HEAD(bucket, kcme, next); + + return 1; +} + +void +kc_map_decr(const struct bstring *key) +{ + struct kc_map_entry *kcme, *prev; + struct kcme_slh *bucket; + + ASSERT(key->len <= MAX_KEY_LEN); + + bucket = _get_bucket(key); + + /* iterate through bucket looking for item */ + for (prev = NULL, kcme = STAILQ_FIRST(bucket); kcme != NULL; + prev = kcme, kcme = STAILQ_NEXT(kcme, next)) { + if ((key->len == kcme->klen) && cc_memcmp(key->data, kcme->key, key->len) == 0) { + /* found item */ + if (--(kcme->count) == 0) { + /* remove entry */ + if (prev == NULL) { + STAILQ_REMOVE_HEAD(bucket, next); + } else { + STAILQ_REMOVE_AFTER(bucket, prev, next); + } + } + + return; + } + } + + /* item not found, should never happen for hotkey detection scheme */ + ASSERT(false); +} diff --git a/src/hotkey/kc_map.h b/src/hotkey/kc_map.h new file mode 100644 index 000000000..a7228857c --- /dev/null +++ b/src/hotkey/kc_map.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +/* + * The kc_map module provides a utility for counting the frequency at which keys + * appear. When a key is sampled, it should be incremented in the table after + * enqueueing it. When a key is dequeued, it should be decremented from the + * table. + */ + +struct bstring; + +/* setup/teardown module */ +void kc_map_setup(uint32_t size, uint32_t poolsize); +void kc_map_teardown(void); + +/* increment count and return count, insert into table if count == 0 */ +uint32_t kc_map_incr(const struct bstring *key); + +/* decrement count, remove from table if count == 0 after decr */ +void kc_map_decr(const struct bstring *key); diff --git a/src/hotkey/key_window.c b/src/hotkey/key_window.c new file mode 100644 index 000000000..3b19086a2 --- /dev/null +++ b/src/hotkey/key_window.c @@ -0,0 +1,195 @@ +#include "key_window.h" +#include "constant.h" + +#include +#include +#include +#include + +#define KEY_WINDOW_MODULE_NAME "hotkey::key_window" + +static bool key_window_init = false; +static int key_window_size = 0; + +FREEPOOL(kwn_pool, kwnq, key_window_node); +static struct kwn_pool kwnp; +static bool kwnp_init = false; + +struct key_window_node { + char key[MAX_KEY_LEN]; + uint32_t nkey; + STAILQ_ENTRY(key_window_node) next; +}; + +STAILQ_HEAD(key_window, key_window_node); +struct key_window q = STAILQ_HEAD_INITIALIZER(q); + +static void +key_window_node_reset(struct key_window_node *kwn) +{ + kwn->nkey = 0; +} + +static struct key_window_node * +key_window_node_create(void) +{ + struct key_window_node *kwn = cc_alloc(sizeof(*kwn)); + + if (kwn == NULL) { + return NULL; + } + + key_window_node_reset(kwn); + + return kwn; +} + +static void +key_window_node_destroy(struct key_window_node **key_window_node) +{ + struct key_window_node *kwn = *key_window_node; + ASSERT(kwn != NULL); + + cc_free(kwn); + *key_window_node = NULL; +} + +static void +key_window_node_pool_destroy(void) +{ + struct key_window_node *kwn, *tkwn; + + if (!kwnp_init) { + log_warn("key_window_node pool was not created, ignore"); + return; + } + + log_info("destroying key_window_node pool: free %"PRIu32, kwnp.nfree); + + FREEPOOL_DESTROY(kwn, tkwn, &kwnp, next, key_window_node_destroy); + kwnp_init = false; +} + +static void +key_window_node_pool_create(uint32_t max) +{ + struct key_window_node *kwn; + + if (kwnp_init) { + log_warn("key_window_node pool has already been created, re-creating"); + key_window_node_pool_destroy(); + } + + log_info("creating key_window_node pool: max %"PRIu32, max); + + FREEPOOL_CREATE(&kwnp, max); + kwnp_init = true; + + FREEPOOL_PREALLOC(kwn, &kwnp, max, next, key_window_node_create); + if (kwnp.nfree < max) { + log_crit("cannot preallocate key_window_node pool, OOM. abort"); + exit(EXIT_FAILURE); + } +} + +static struct key_window_node * +key_window_node_borrow(void) +{ + struct key_window_node *kwn; + + FREEPOOL_BORROW(kwn, &kwnp, next, key_window_node_create); + if (kwn == NULL) { + log_debug("borrow key_window_node failed: OOM"); + return NULL; + } + key_window_node_reset(kwn); + + return kwn; +} + +static void +key_window_node_return(struct key_window_node **key_window_node) +{ + struct key_window_node *kwn = *key_window_node; + + if (kwn == NULL) { + return; + } + + FREEPOOL_RETURN(kwn, &kwnp, next); + + *key_window_node = NULL; +} + +void +key_window_setup(uint32_t poolsize) +{ + log_info("set up the %s module", KEY_WINDOW_MODULE_NAME); + + if (key_window_init) { + log_warn("%s has already been setup, overwrite", KEY_WINDOW_MODULE_NAME); + } + + key_window_node_pool_create(poolsize); + key_window_size = 0; + STAILQ_INIT(&q); + key_window_init = true; +} + +void +key_window_teardown(void) +{ + struct key_window_node *kwn, *tkwn; + + log_info("tear down the %s module", KEY_WINDOW_MODULE_NAME); + + if (!key_window_init) { + log_warn("%s was not setup", KEY_WINDOW_MODULE_NAME); + return; + } + + /* free all entries in key_window */ + STAILQ_FOREACH_SAFE(kwn, &q, next, tkwn) { + key_window_node_return(&kwn); + } + + key_window_node_pool_destroy(); + key_window_init = false; +} + +void +key_window_push(const struct bstring *key) +{ + struct key_window_node *kwn = key_window_node_borrow(); + + ASSERT(key->len <= MAX_KEY_LEN); + + cc_memcpy(kwn->key, key->data, key->len); + kwn->nkey = key->len; + STAILQ_INSERT_TAIL(&q, kwn, next); + ++key_window_size; +} + +uint32_t +key_window_pop(char *buf) +{ + struct key_window_node *kwn = STAILQ_FIRST(&q); + uint32_t nkey; + + ASSERT(key_window_size > 0); + + cc_memcpy(buf, kwn->key, kwn->nkey); + nkey = kwn->nkey; + + STAILQ_REMOVE_HEAD(&q, next); + key_window_node_return(&kwn); + --key_window_size; + + return nkey; +} + +uint32_t +key_window_len(void) +{ + return key_window_size; +} diff --git a/src/hotkey/key_window.h b/src/hotkey/key_window.h new file mode 100644 index 000000000..44ec97f0c --- /dev/null +++ b/src/hotkey/key_window.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +/* + * The key_window module provides a FIFO key_window interface to help with bookkeeping the + * last N keys provided for hotkey detection. + */ + +struct bstring; + +void key_window_push(const struct bstring *key); +uint32_t key_window_pop(char *buf); /* returns length of key */ +uint32_t key_window_len(void); + +/* Setup/teardown functions, must be called before module is used */ +void key_window_setup(uint32_t poolsize); +void key_window_teardown(void); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 420dde343..42b6bead5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,4 +3,5 @@ include_directories(${include_directories} CHECK_INCLUDES) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}) add_subdirectory(protocol) +add_subdirectory(hotkey) add_subdirectory(storage) diff --git a/test/hotkey/CMakeLists.txt b/test/hotkey/CMakeLists.txt new file mode 100644 index 000000000..109a0e1c2 --- /dev/null +++ b/test/hotkey/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(kc_map) +add_subdirectory(key_window) diff --git a/test/hotkey/kc_map/CMakeLists.txt b/test/hotkey/kc_map/CMakeLists.txt new file mode 100644 index 000000000..505070af4 --- /dev/null +++ b/test/hotkey/kc_map/CMakeLists.txt @@ -0,0 +1,11 @@ +set(suite kc_map) +set(test_name check_${suite}) + +set(source check_${suite}.c) + +add_executable(${test_name} ${source}) +target_link_libraries(${test_name} hotkey) +target_link_libraries(${test_name} ccommon-static ${CHECK_LIBRARIES}) + +add_dependencies(check ${test_name}) +add_test(${test_name} ${test_name}) diff --git a/test/hotkey/kc_map/check_kc_map.c b/test/hotkey/kc_map/check_kc_map.c new file mode 100644 index 000000000..f384e9cb1 --- /dev/null +++ b/test/hotkey/kc_map/check_kc_map.c @@ -0,0 +1,105 @@ +#include + +#include + +#include + +#include + +#include +#include + +#define SUITE_NAME "kc_map" +#define DEBUG_LOG SUITE_NAME ".log" + +#define TEST_TABLE_SIZE 10 + +/* + * utilities + */ +static void +test_setup(void) +{ + kc_map_setup(TEST_TABLE_SIZE, TEST_TABLE_SIZE); +} + +static void +test_teardown(void) +{ + kc_map_teardown(); +} + +static void +test_reset(void) +{ + test_teardown(); + test_setup(); +} + +/************** + * test cases * + **************/ + +START_TEST(test_basic) +{ +#define KEY1 "key1" +#define KEY2 "key22" + uint32_t count; + struct bstring key1 = str2bstr(KEY1), key2 = str2bstr(KEY2); + + test_reset(); + + count = kc_map_incr(&key1); + ck_assert_int_eq(count, 1); + + count = kc_map_incr(&key2); + ck_assert_int_eq(count, 1); + + count = kc_map_incr(&key1); + ck_assert_int_eq(count, 2); + + kc_map_decr(&key1); + count = kc_map_incr(&key1); + ck_assert_int_eq(count, 2); +#undef KEY1 +#undef KEY2 +} +END_TEST + +/* + * test suite + */ +static Suite * +kc_map_suite(void) +{ + Suite *s = suite_create(SUITE_NAME); + + /* basic functionality */ + TCase *tc_basic_kc_map = tcase_create("basic kc_map"); + suite_add_tcase(s, tc_basic_kc_map); + + tcase_add_test(tc_basic_kc_map, test_basic); + + return s; +} + +int +main(void) +{ + int nfail; + + /* setup */ + test_setup(); + + Suite *suite = kc_map_suite(); + SRunner *srunner = srunner_create(suite); + srunner_set_log(srunner, DEBUG_LOG); + srunner_run_all(srunner, CK_ENV); /* set CK_VERBOSITY in ENV to customize */ + nfail = srunner_ntests_failed(srunner); + srunner_free(srunner); + + /* teardown */ + test_teardown(); + + return (nfail == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/test/hotkey/key_window/CMakeLists.txt b/test/hotkey/key_window/CMakeLists.txt new file mode 100644 index 000000000..cd4b688e4 --- /dev/null +++ b/test/hotkey/key_window/CMakeLists.txt @@ -0,0 +1,11 @@ +set(suite key_window) +set(test_name check_${suite}) + +set(source check_${suite}.c) + +add_executable(${test_name} ${source}) +target_link_libraries(${test_name} hotkey) +target_link_libraries(${test_name} ccommon-static ${CHECK_LIBRARIES}) + +add_dependencies(check ${test_name}) +add_test(${test_name} ${test_name}) diff --git a/test/hotkey/key_window/check_key_window.c b/test/hotkey/key_window/check_key_window.c new file mode 100644 index 000000000..3929ae5d6 --- /dev/null +++ b/test/hotkey/key_window/check_key_window.c @@ -0,0 +1,142 @@ +#include + +#include + +#include + +#include + +#include +#include +#include + +#define SUITE_NAME "key_window" +#define DEBUG_LOG SUITE_NAME ".log" + +#define TEST_KEY_WINDOW_SIZE 10 + +/* + * utilities + */ +static void +test_setup(void) +{ + key_window_setup(TEST_KEY_WINDOW_SIZE); +} + +static void +test_teardown(void) +{ + key_window_teardown(); +} + +static void +test_reset(void) +{ + test_teardown(); + test_setup(); +} + +/************** + * test cases * + **************/ + +START_TEST(test_basic) +{ +#define KEY1 "key1" + struct bstring key1 = str2bstr(KEY1); + char buf[MAX_KEY_LEN]; + uint32_t key_window_pop_len; + + test_reset(); + + ck_assert_int_eq(key_window_len(), 0); + + key_window_push(&key1); + ck_assert_int_eq(key_window_len(), 1); + key_window_pop_len = key_window_pop(buf); + + ck_assert_int_eq(key_window_len(), 0); + ck_assert_int_eq(key_window_pop_len, key1.len); + ck_assert(strncmp(key1.data, buf, key1.len) == 0); +#undef KEY1 +} +END_TEST + +START_TEST(test_multiple) +{ +#define KEY1 "key1" +#define KEY2 "key22" +#define KEY3 "key333" + char buf[MAX_KEY_LEN]; + uint32_t key_window_pop_len; + struct bstring key1 = str2bstr(KEY1), key2 = str2bstr(KEY2), + key3 = str2bstr(KEY3); + + test_reset(); + + ck_assert_int_eq(key_window_len(), 0); + + key_window_push(&key1); + key_window_push(&key2); + key_window_push(&key3); + ck_assert_int_eq(key_window_len(), 3); + + key_window_pop_len = key_window_pop(buf); + ck_assert_int_eq(key_window_pop_len, key1.len); + ck_assert(strncmp(key1.data, buf, key1.len) == 0); + ck_assert_int_eq(key_window_len(), 2); + + key_window_pop_len = key_window_pop(buf); + ck_assert_int_eq(key_window_pop_len, key2.len); + ck_assert(strncmp(key2.data, buf, key2.len) == 0); + ck_assert_int_eq(key_window_len(), 1); + + key_window_pop_len = key_window_pop(buf); + ck_assert_int_eq(key_window_pop_len, key3.len); + ck_assert(strncmp(key3.data, buf, key3.len) == 0); + ck_assert_int_eq(key_window_len(), 0); +#undef KEY1 +#undef KEY2 +#undef KEY3 +} +END_TEST + +/* + * test suite + */ +static Suite * +key_window_suite(void) +{ + Suite *s = suite_create(SUITE_NAME); + + /* basic key_window functionality */ + TCase *tc_basic_key_window = tcase_create("basic key_window"); + suite_add_tcase(s, tc_basic_key_window); + + tcase_add_test(tc_basic_key_window, test_basic); + tcase_add_test(tc_basic_key_window, test_multiple); + + return s; +} + +int +main(void) +{ + int nfail; + + /* setup */ + test_setup(); + + Suite *suite = key_window_suite(); + SRunner *srunner = srunner_create(suite); + srunner_set_log(srunner, DEBUG_LOG); + srunner_run_all(srunner, CK_ENV); /* set CK_VERBOSITY in ENV to customize */ + nfail = srunner_ntests_failed(srunner); + srunner_free(srunner); + + /* teardown */ + test_teardown(); + + return (nfail == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/test/protocol/admin/check_admin.c b/test/protocol/admin/check_admin.c index c72a1cb69..4c0edc90e 100644 --- a/test/protocol/admin/check_admin.c +++ b/test/protocol/admin/check_admin.c @@ -147,7 +147,7 @@ main(void) Suite *suite = admin_suite(); SRunner *srunner = srunner_create(suite); srunner_set_log(srunner, DEBUG_LOG); - srunner_run_all(srunner, CK_ENV); /* set CK_VEBOSITY in ENV to customize */ + srunner_run_all(srunner, CK_ENV); /* set CK_VERBOSITY in ENV to customize */ nfail = srunner_ntests_failed(srunner); srunner_free(srunner);