diff --git a/main/event.c b/main/event.c index 366834ea64b51cf42e47a78077d50ab489069cec..51a3a45ab580598168483098bf70e960548ab7bd 100644 --- a/main/event.c +++ b/main/event.c @@ -350,14 +350,69 @@ static void ast_event_ie_val_destroy(struct ast_event_ie_val *ie_val) ast_free(ie_val); } +/*! + * \internal + * \brief Check if an ie_val matches a subscription + * + * \param sub subscription to check against + * \param ie_val IE value to check + * + * \retval 0 not matched + * \retval non-zero matched + */ +static int match_ie_val_to_sub(const struct ast_event_sub *sub, const struct ast_event_ie_val *ie_val) +{ + const struct ast_event_ie_val *sub_ie_val; + int res = 1; + + AST_LIST_TRAVERSE(&sub->ie_vals, sub_ie_val, entry) { + if (sub_ie_val->ie_type == ie_val->ie_type) { + break; + } + } + + if (!sub_ie_val) { + /* This subscriber doesn't care about this IE, so consider + * it matched. */ + return 1; + } + + switch (ie_val->ie_pltype) { + case AST_EVENT_IE_PLTYPE_UINT: + res = (ie_val->payload.uint != sub_ie_val->payload.uint); + break; + case AST_EVENT_IE_PLTYPE_BITFLAGS: + /* + * If the subscriber has requested *any* of the bitflags we are providing, + * then it's a match. + */ + res = !(ie_val->payload.uint & sub_ie_val->payload.uint); + break; + case AST_EVENT_IE_PLTYPE_STR: + res = strcmp(ie_val->payload.str, sub_ie_val->payload.str); + break; + case AST_EVENT_IE_PLTYPE_RAW: + res = memcmp(ie_val->payload.raw, + sub_ie_val->payload.raw, ie_val->raw_datalen); + break; + case AST_EVENT_IE_PLTYPE_EXISTS: + case AST_EVENT_IE_PLTYPE_UNKNOWN: + break; + } + + return res; +} + enum ast_event_subscriber_res ast_event_check_subscriber(enum ast_event_type type, ...) { va_list ap; enum ast_event_ie_type ie_type; enum ast_event_subscriber_res res = AST_EVENT_SUB_NONE; - struct ast_event_ie_val *ie_val, *sub_ie_val; + struct ast_event_ie_val *ie_val; struct ast_event_sub *sub; AST_LIST_HEAD_NOLOCK_STATIC(ie_vals, ast_event_ie_val); + const enum ast_event_type event_types[] = { type, AST_EVENT_ALL }; + int i; if (type >= AST_EVENT_TOTAL) { ast_log(LOG_ERROR, "%u is an invalid type!\n", type); @@ -405,74 +460,42 @@ enum ast_event_subscriber_res ast_event_check_subscriber(enum ast_event_type typ } va_end(ap); - AST_RWDLLIST_RDLOCK(&ast_event_subs[type]); - AST_RWDLLIST_TRAVERSE(&ast_event_subs[type], sub, entry) { - AST_LIST_TRAVERSE(&ie_vals, ie_val, entry) { - int break_out = 0; - - AST_LIST_TRAVERSE(&sub->ie_vals, sub_ie_val, entry) { - if (sub_ie_val->ie_type == ie_val->ie_type) { + for (i = 0; i < ARRAY_LEN(event_types); i++) { + AST_RWDLLIST_RDLOCK(&ast_event_subs[event_types[i]]); + AST_RWDLLIST_TRAVERSE(&ast_event_subs[event_types[i]], sub, entry) { + AST_LIST_TRAVERSE(&ie_vals, ie_val, entry) { + if (match_ie_val_to_sub(sub, ie_val)) { break; } } - if (!sub_ie_val) { - /* This subscriber doesn't care about this IE, so consider - * it matched. */ - continue; - } - - switch (ie_val->ie_pltype) { - case AST_EVENT_IE_PLTYPE_UINT: - break_out = (ie_val->payload.uint != sub_ie_val->payload.uint); - break; - case AST_EVENT_IE_PLTYPE_BITFLAGS: - /* if the subscriber has requested *any* of the bitflags we are providing, - * then it's a match - */ - break_out = (ie_val->payload.uint & sub_ie_val->payload.uint); - break; - case AST_EVENT_IE_PLTYPE_STR: - break_out = strcmp(ie_val->payload.str, sub_ie_val->payload.str); - break; - case AST_EVENT_IE_PLTYPE_RAW: - break_out = memcmp(ie_val->payload.raw, - sub_ie_val->payload.raw, ie_val->raw_datalen); - break; - case AST_EVENT_IE_PLTYPE_EXISTS: - /* The subscriber doesn't actually care what the value is */ - break_out = 1; - break; - case AST_EVENT_IE_PLTYPE_UNKNOWN: - break; - } - - if (break_out) { + if (!ie_val) { + /* Everything matched. */ break; } } - - if (!ie_val) { - /* Everything matched */ + AST_RWDLLIST_UNLOCK(&ast_event_subs[event_types[i]]); + if (sub) { break; } } - AST_RWDLLIST_UNLOCK(&ast_event_subs[type]); - - if (sub) { - /* All parameters were matched */ - return AST_EVENT_SUB_EXISTS; - } - AST_RWDLLIST_RDLOCK(&ast_event_subs[AST_EVENT_ALL]); - if (!AST_DLLIST_EMPTY(&ast_event_subs[AST_EVENT_ALL])) { - res = AST_EVENT_SUB_EXISTS; - } - AST_RWDLLIST_UNLOCK(&ast_event_subs[AST_EVENT_ALL]); - - return res; + return sub ? AST_EVENT_SUB_EXISTS : AST_EVENT_SUB_NONE; } +/*! + * \internal + * \brief Check if an ie_val matches an event + * + * \param event event to check against + * \param ie_val IE value to check + * \param event2 optional event, if specified, the value to compare against will be pulled + * from this event instead of from the ie_val structure. In this case, only the IE + * type and payload type will be pulled from ie_val. + * + * \retval 0 not matched + * \retval non-zero matched + */ static int match_ie_val(const struct ast_event *event, const struct ast_event_ie_val *ie_val, const struct ast_event *event2) { @@ -488,8 +511,9 @@ static int match_ie_val(const struct ast_event *event, { uint32_t flags = event2 ? ast_event_get_ie_uint(event2, ie_val->ie_type) : ie_val->payload.uint; - /* if the subscriber has requested *any* of the bitflags that this event provides, - * then it's a match + /* + * If the subscriber has requested *any* of the bitflags that this event provides, + * then it's a match. */ return (flags & ast_event_get_ie_bitflags(event, ie_val->ie_type)) ? 1 : 0; } @@ -1332,32 +1356,28 @@ static int handle_event(void *data) { struct ast_event_ref *event_ref = data; struct ast_event_sub *sub; - uint16_t host_event_type; - - host_event_type = ntohs(event_ref->event->type); + const enum ast_event_type event_types[] = { + ntohs(event_ref->event->type), + AST_EVENT_ALL + }; + int i; - /* Subscribers to this specific event first */ - AST_RWDLLIST_RDLOCK(&ast_event_subs[host_event_type]); - AST_RWDLLIST_TRAVERSE(&ast_event_subs[host_event_type], sub, entry) { - struct ast_event_ie_val *ie_val; - AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) { - if (!match_ie_val(event_ref->event, ie_val, NULL)) { - break; + for (i = 0; i < ARRAY_LEN(event_types); i++) { + AST_RWDLLIST_RDLOCK(&ast_event_subs[event_types[i]]); + AST_RWDLLIST_TRAVERSE(&ast_event_subs[event_types[i]], sub, entry) { + struct ast_event_ie_val *ie_val; + AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) { + if (!match_ie_val(event_ref->event, ie_val, NULL)) { + break; + } } + if (ie_val) { + continue; + } + sub->cb(event_ref->event, sub->userdata); } - if (ie_val) { - continue; - } - sub->cb(event_ref->event, sub->userdata); - } - AST_RWDLLIST_UNLOCK(&ast_event_subs[host_event_type]); - - /* Now to subscribers to all event types */ - AST_RWDLLIST_RDLOCK(&ast_event_subs[AST_EVENT_ALL]); - AST_RWDLLIST_TRAVERSE(&ast_event_subs[AST_EVENT_ALL], sub, entry) { - sub->cb(event_ref->event, sub->userdata); + AST_RWDLLIST_UNLOCK(&ast_event_subs[event_types[i]]); } - AST_RWDLLIST_UNLOCK(&ast_event_subs[AST_EVENT_ALL]); ao2_ref(event_ref, -1); diff --git a/tests/test_event.c b/tests/test_event.c index 47895b56414b007261731322f1bdbb384f043f8e..6a557b5251bd5c2ce15423618e3cfa0514aefeee 100644 --- a/tests/test_event.c +++ b/tests/test_event.c @@ -23,6 +23,23 @@ * \author Russell Bryant <russell@digium.com> * * \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; }