From 52a8ddba515d5ea2bce69c8ba1d15b926f680f93 Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Thu, 22 Apr 2010 21:06:53 +0000 Subject: Add ast_event subscription unit test and fix some ast_event API bugs. This patch introduces another test in test_event.c that exercises most of the subscription related ast_event API calls. I made some minor additions to the existing event allocation test to increase API coverage by the test code. Finally, I made a list in a comment of API calls not yet touched by the test module as a to-do list for future test development. During the development of this test code, I discovered a number of bugs in the event API. 1) subscriptions to AST_EVENT_ALL were not handled appropriately in a couple of different places. The API allows a subscription to all event types, but with IE parameters, just as if it was a subscription to a specific event type. However, the parameters were being ignored. This affected ast_event_check_subscriber() and event distribution to subscribers. 2) Some of the logic in ast_event_check_subscriber() for checking subscriptions against query parameters was wrong. Review: https://reviewboard.asterisk.org/r/617/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@258632 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- tests/test_event.c | 379 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 363 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/tests/test_event.c b/tests/test_event.c index 47895b564..6a557b525 100644 --- a/tests/test_event.c +++ b/tests/test_event.c @@ -23,6 +23,23 @@ * \author Russell Bryant * * \ingroup tests + * + * \todo API Calls not yet touched by a test: XXX TODO + * - ast_event_queue_and_cache() + * - ast_event_get_cached() + * - ast_event_report_subs() + * - ast_event_dump_cache() + * - ast_event_get_ie_type_name() + * - ast_event_get_ie_pltype() + * - ast_event_str_to_event_type() + * - ast_event_str_to_ie_type() + * - ast_event_iterator_init() + * - ast_event_iterator_next() + * - ast_event_iterator_get_ie_type() + * - ast_event_iterator_get_ie_uint() + * - ast_event_iterator_get_ie_bitflags() + * - ast_event_iterator_get_ie_str() + * - ast_event_iterator_get_ie_raw() */ /*** MODULEINFO @@ -39,8 +56,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/event.h" static int check_event(struct ast_event *event, struct ast_test *test, - enum ast_event_type expected_type, const char *str, - uint32_t uint) + enum ast_event_type expected_type, const char *type_name, + const char *str, uint32_t uint, uint32_t bitflags) { enum ast_event_type type; const void *foo; @@ -53,32 +70,45 @@ static int check_event(struct ast_event *event, struct ast_test *test, return -1; } - /* Check #2: Check for automatically included EID */ + /* Check #2: Check string representation of event type */ + if (strcmp(type_name, ast_event_get_type_name(event))) { + ast_test_status_update(test, "Didn't get expected type name: '%s' != '%s'\n", + type_name, ast_event_get_type_name(event)); + return -1; + } + + /* Check #3: Check for automatically included EID */ if (memcmp(&ast_eid_default, ast_event_get_ie_raw(event, AST_EVENT_IE_EID), sizeof(ast_eid_default))) { ast_test_status_update(test, "Failed to get EID\n"); return -1; } - /* Check #3: Check for the string IE */ + /* Check #4: Check for the string IE */ if (strcmp(str, ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX))) { ast_test_status_update(test, "Failed to get string IE.\n"); return -1; } - /* Check #4: Check for the uint IE */ + /* Check #5: Check for the uint IE */ if (uint != ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS)) { ast_test_status_update(test, "Failed to get uint IE.\n"); return -1; } - /* Check #5: Check if a check for a str IE that isn't there works */ + /* Check #6: Check for the bitflags IE */ + if (bitflags != ast_event_get_ie_bitflags(event, AST_EVENT_IE_OLDMSGS)) { + ast_test_status_update(test, "Failed to get bitflags IE.\n"); + return -1; + } + + /* Check #7: Check if a check for a str IE that isn't there works */ if ((foo = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE))) { ast_test_status_update(test, "DEVICE IE check returned non-NULL %p\n", foo); return -1; } - /* Check #6: Check if a check for a uint IE that isn't there returns 0 */ - if (ast_event_get_ie_uint(event, AST_EVENT_IE_OLDMSGS)) { + /* Check #8: Check if a check for a uint IE that isn't there returns 0 */ + if (ast_event_get_ie_uint(event, AST_EVENT_IE_STATE)) { ast_test_status_update(test, "OLDMSGS IE should be 0\n"); return -1; } @@ -88,14 +118,18 @@ static int check_event(struct ast_event *event, struct ast_test *test, return 0; } +/*! + * \internal + */ AST_TEST_DEFINE(event_new_test) { enum ast_test_result_state res = AST_TEST_PASS; - struct ast_event *event = NULL; + struct ast_event *event = NULL, *event2 = NULL; static const enum ast_event_type type = AST_EVENT_CUSTOM; static const char str[] = "SIP/alligatormittens"; static const uint32_t uint = 0xb00bface; + static const uint32_t bitflags = 0x12488421; switch (cmd) { case TEST_INIT: @@ -137,56 +171,369 @@ AST_TEST_DEFINE(event_new_test) goto return_cleanup; } + if (ast_event_append_ie_bitflags(&event, AST_EVENT_IE_OLDMSGS, bitflags)) { + ast_test_status_update(test, "Failed to append bitflags IE\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + if (ast_event_append_eid(&event)) { ast_test_status_update(test, "Failed to append EID\n"); res = AST_TEST_FAIL; goto return_cleanup; } - if (check_event(event, test, type, str, uint)) { + if (check_event(event, test, type, "Custom", str, uint, bitflags)) { ast_test_status_update(test, "Dynamically generated event broken\n"); res = AST_TEST_FAIL; goto return_cleanup; } - ast_event_destroy(event); - event = NULL; - - event = ast_event_new(type, + event2 = ast_event_new(type, AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, str, AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, uint, + AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_BITFLAGS, bitflags, AST_EVENT_IE_END); - if (!event) { + if (!event2) { ast_test_status_update(test, "Failed to allocate ast_event object.\n"); res = AST_TEST_FAIL; goto return_cleanup; } - if (check_event(event, test, type, str, uint)) { + if (check_event(event2, test, type, "Custom", str, uint, bitflags)) { ast_test_status_update(test, "Statically generated event broken\n"); res = AST_TEST_FAIL; goto return_cleanup; } + if (ast_event_get_size(event) != ast_event_get_size(event2)) { + ast_test_status_update(test, "Events expected to be identical have different size: %d != %d\n", + (int) ast_event_get_size(event), + (int) ast_event_get_size(event2)); + res = AST_TEST_FAIL; + goto return_cleanup; + } + return_cleanup: if (event) { ast_event_destroy(event); event = NULL; } + if (event2) { + ast_event_destroy(event2); + event2 = NULL; + } + + return res; +} + +struct event_sub_data { + unsigned int count; +}; + +static void event_sub_cb(const struct ast_event *event, void *d) +{ + struct event_sub_data *data = d; + + data->count++; +} + +/*! + * \internal + * \brief Test event subscriptions + * + * - Query for existing Subscriptions: + * - ast_event_check_subscriber() + */ +AST_TEST_DEFINE(event_sub_test) +{ + enum ast_test_result_state res = AST_TEST_PASS; + struct ast_event *event; + int i; + enum ast_event_subscriber_res sub_res; + static struct { + struct ast_event_sub *sub; + struct event_sub_data data; + const unsigned int expected_count; + } test_subs[] = { + [0] = { + .expected_count = 3, + }, + [1] = { + .expected_count = 1, + }, + [2] = { + .expected_count = 2, + }, + }; + + switch (cmd) { + case TEST_INIT: + info->name = "ast_event_subscribe_test"; + info->category = "main/event/"; + info->summary = "Test event subscriptions"; + info->description = + "This test exercises the API calls that allow subscriptions " + "to events."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + /* + * Subscription #1: + * - allocate normally + * - subscribe to all CUSTOM events + * + * Subscription #2: + * - allocate dynamically + * - subscribe to all CUSTOM events + * - add payload checks + * + * Subscription #3: + * - allocate normally + * - subscribe to all events with an IE check + */ + + test_subs[0].sub = ast_event_subscribe(AST_EVENT_CUSTOM, event_sub_cb, "test_sub", &test_subs[0].data, + AST_EVENT_IE_END); + if (!test_subs[0].sub) { + ast_test_status_update(test, "Failed to create test_subs[0].sub\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + if (strcmp(ast_event_subscriber_get_description(test_subs[0].sub), "test_sub")) { + ast_test_status_update(test, + "Unexpected subscription description on test_subs[0].sub\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + test_subs[1].sub = ast_event_subscribe_new(AST_EVENT_CUSTOM, event_sub_cb, &test_subs[1].data); + if (!test_subs[1].sub) { + ast_test_status_update(test, "Failed to create test_subs[1].sub\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + /* For the sake of exercising destruction before activation */ + ast_event_sub_destroy(test_subs[1].sub); + + test_subs[1].sub = ast_event_subscribe_new(AST_EVENT_CUSTOM, event_sub_cb, &test_subs[1].data); + if (!test_subs[1].sub) { + ast_test_status_update(test, "Failed to create test_subs[1].sub\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + if (strcmp(ast_event_subscriber_get_description(test_subs[1].sub), "")) { + ast_event_sub_destroy(test_subs[1].sub); + test_subs[1].sub = NULL; + ast_test_status_update(test, + "Unexpected subscription description on test_subs[1].sub\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + if (ast_event_sub_append_ie_uint(test_subs[1].sub, AST_EVENT_IE_NEWMSGS, 3)) { + ast_event_sub_destroy(test_subs[1].sub); + test_subs[1].sub = NULL; + ast_test_status_update(test, "Failed to append uint IE to test_subs[1].sub\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + if (ast_event_sub_append_ie_bitflags(test_subs[1].sub, AST_EVENT_IE_NEWMSGS, 1)) { + ast_event_sub_destroy(test_subs[1].sub); + test_subs[1].sub = NULL; + ast_test_status_update(test, "Failed to append bitflags IE to test_subs[1].sub\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + if (ast_event_sub_append_ie_str(test_subs[1].sub, AST_EVENT_IE_DEVICE, "FOO/bar")) { + ast_event_sub_destroy(test_subs[1].sub); + test_subs[1].sub = NULL; + ast_test_status_update(test, "Failed to append str IE to test_subs[1].sub\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + if (ast_event_sub_append_ie_raw(test_subs[1].sub, AST_EVENT_IE_MAILBOX, "800 km", + strlen("800 km"))) { + ast_event_sub_destroy(test_subs[1].sub); + test_subs[1].sub = NULL; + ast_test_status_update(test, "Failed to append raw IE to test_subs[1].sub\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + if (ast_event_sub_append_ie_exists(test_subs[1].sub, AST_EVENT_IE_DEVICE)) { + ast_event_sub_destroy(test_subs[1].sub); + test_subs[1].sub = NULL; + ast_test_status_update(test, "Failed to append exists IE to test_subs[1].sub\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + if (ast_event_sub_activate(test_subs[1].sub)) { + ast_event_sub_destroy(test_subs[1].sub); + test_subs[1].sub = NULL; + ast_test_status_update(test, "Failed to activate test_subs[1].sub\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + test_subs[2].sub = ast_event_subscribe(AST_EVENT_ALL, event_sub_cb, "test_sub", &test_subs[2].data, + AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "FOO/bar", + AST_EVENT_IE_END); + if (!test_subs[2].sub) { + ast_test_status_update(test, "Failed to create test_subs[2].sub\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + /* + * Exercise the API call to check for existing subscriptions. + */ + + sub_res = ast_event_check_subscriber(AST_EVENT_CUSTOM, + AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "FOO/bar", + AST_EVENT_IE_END); + if (sub_res != AST_EVENT_SUB_EXISTS) { + ast_test_status_update(test, "subscription did not exist\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + sub_res = ast_event_check_subscriber(AST_EVENT_CUSTOM, + AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "BOOBS", + AST_EVENT_IE_END); + if (sub_res != AST_EVENT_SUB_NONE) { + ast_test_status_update(test, "Someone subscribed to updates on boobs, lol? (%d)\n", sub_res); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + /* + * Fire off some events and track what was received in the callback + * + * event #1: + * - simple custom event (will match sub 1 and 3) + * + * event #2: + * - custom event with payloads that satisfy every payload check + * for sub #2 (will match sub 1, 2, and 3) + * + * event #3: + * - custom event that should only match sub #1 + */ + + event = ast_event_new(AST_EVENT_CUSTOM, + AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "FOO/bar", + AST_EVENT_IE_END); + if (!event) { + ast_test_status_update(test, "Failed to create event\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + if (ast_event_queue(event)) { + ast_event_destroy(event); + event = NULL; + ast_test_status_update(test, "Failed to queue event\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + event = ast_event_new(AST_EVENT_CUSTOM, + AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "FOO/bar", + AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_RAW, "800 km", strlen("800 km"), + AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, 3, + AST_EVENT_IE_END); + if (!event) { + ast_test_status_update(test, "Failed to create event\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + if (ast_event_queue(event)) { + ast_event_destroy(event); + event = NULL; + ast_test_status_update(test, "Failed to queue event\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + + event = ast_event_new(AST_EVENT_CUSTOM, + AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "blah", + AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_RAW, "801 km", strlen("801 km"), + AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, 0, + AST_EVENT_IE_END); + if (!event) { + ast_test_status_update(test, "Failed to create event\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + if (ast_event_queue(event)) { + ast_event_destroy(event); + event = NULL; + ast_test_status_update(test, "Failed to queue event\n"); + res = AST_TEST_FAIL; + goto return_cleanup; + } + event = NULL; + + /* + * Check the results of the test. + * + * First of all, event distribution is asynchronous from the event producer, + * so knowing when to continue from here and check results is an instance of + * the halting problem. A few seconds really should be more than enough time. + * If something was actually blocking event distribution that long, I would call + * it a bug. + * + * Expected results: + * - sub 1, 2 events + * - sub 2, 1 event + * - sub 3, 2 events + */ + + ast_test_status_update(test, "Sleeping a few seconds to allow event propagation...\n"); + sleep(3); + + for (i = 0; i < ARRAY_LEN(test_subs); i++) { + if (test_subs[i].data.count != test_subs[i].expected_count) { + ast_test_status_update(test, "Unexpected callback count, %u != %u for #%d\n", + test_subs[i].data.count, test_subs[i].expected_count, i); + res = AST_TEST_FAIL; + } + } + +return_cleanup: + for (i = 0; i < ARRAY_LEN(test_subs); i++) { + if (test_subs[i].sub) { + test_subs[i].sub = ast_event_unsubscribe(test_subs[i].sub); + } + } + return res; } static int unload_module(void) { AST_TEST_UNREGISTER(event_new_test); + AST_TEST_UNREGISTER(event_sub_test); + return 0; } static int load_module(void) { AST_TEST_REGISTER(event_new_test); + AST_TEST_REGISTER(event_sub_test); + return AST_MODULE_LOAD_SUCCESS; } -- cgit v1.2.3