Add ast_event subscription unit test and fix some ast_event API bugs.
[asterisk/asterisk.git] / tests / test_event.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2010, Digium, Inc.
5  *
6  * Russell Bryant <russell@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  * \brief Tests for the ast_event API
22  *
23  * \author Russell Bryant <russell@digium.com>
24  *
25  * \ingroup tests
26  *
27  * \todo API Calls not yet touched by a test: XXX TODO
28  *   - ast_event_queue_and_cache()
29  *   - ast_event_get_cached()
30  *   - ast_event_report_subs()
31  *   - ast_event_dump_cache()
32  *   - ast_event_get_ie_type_name()
33  *   - ast_event_get_ie_pltype()
34  *   - ast_event_str_to_event_type()
35  *   - ast_event_str_to_ie_type()
36  *   - ast_event_iterator_init()
37  *   - ast_event_iterator_next()
38  *   - ast_event_iterator_get_ie_type()
39  *   - ast_event_iterator_get_ie_uint()
40  *   - ast_event_iterator_get_ie_bitflags()
41  *   - ast_event_iterator_get_ie_str()
42  *   - ast_event_iterator_get_ie_raw()
43  */
44
45 /*** MODULEINFO
46         <depend>TEST_FRAMEWORK</depend>
47  ***/
48
49 #include "asterisk.h"
50
51 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
52
53 #include "asterisk/module.h"
54 #include "asterisk/utils.h"
55 #include "asterisk/test.h"
56 #include "asterisk/event.h"
57
58 static int check_event(struct ast_event *event, struct ast_test *test,
59                 enum ast_event_type expected_type, const char *type_name,
60                 const char *str, uint32_t uint, uint32_t bitflags)
61 {
62         enum ast_event_type type;
63         const void *foo;
64
65         /* Check #1: Ensure event type is set properly. */
66         type = ast_event_get_type(event);
67         if (ast_event_get_type(event) != type) {
68                 ast_test_status_update(test, "Expected event type: '%d', got '%d'\n",
69                                 expected_type, type);
70                 return -1;
71         }
72
73         /* Check #2: Check string representation of event type */
74         if (strcmp(type_name, ast_event_get_type_name(event))) {
75                 ast_test_status_update(test, "Didn't get expected type name: '%s' != '%s'\n",
76                                 type_name, ast_event_get_type_name(event));
77                 return -1;
78         }
79
80         /* Check #3: Check for automatically included EID */
81         if (memcmp(&ast_eid_default, ast_event_get_ie_raw(event, AST_EVENT_IE_EID), sizeof(ast_eid_default))) {
82                 ast_test_status_update(test, "Failed to get EID\n");
83                 return -1;
84         }
85
86         /* Check #4: Check for the string IE */
87         if (strcmp(str, ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX))) {
88                 ast_test_status_update(test, "Failed to get string IE.\n");
89                 return -1;
90         }
91
92         /* Check #5: Check for the uint IE */
93         if (uint != ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS)) {
94                 ast_test_status_update(test, "Failed to get uint IE.\n");
95                 return -1;
96         }
97
98         /* Check #6: Check for the bitflags IE */
99         if (bitflags != ast_event_get_ie_bitflags(event, AST_EVENT_IE_OLDMSGS)) {
100                 ast_test_status_update(test, "Failed to get bitflags IE.\n");
101                 return -1;
102         }
103
104         /* Check #7: Check if a check for a str IE that isn't there works */
105         if ((foo = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE))) {
106                 ast_test_status_update(test, "DEVICE IE check returned non-NULL %p\n", foo);
107                 return -1;
108         }
109
110         /* Check #8: Check if a check for a uint IE that isn't there returns 0 */
111         if (ast_event_get_ie_uint(event, AST_EVENT_IE_STATE)) {
112                 ast_test_status_update(test, "OLDMSGS IE should be 0\n");
113                 return -1;
114         }
115
116         ast_test_status_update(test, "Event looks good.\n");
117
118         return 0;
119 }
120
121 /*!
122  * \internal
123  */
124 AST_TEST_DEFINE(event_new_test)
125 {
126         enum ast_test_result_state res = AST_TEST_PASS;
127         struct ast_event *event = NULL, *event2 = NULL;
128
129         static const enum ast_event_type type = AST_EVENT_CUSTOM;
130         static const char str[] = "SIP/alligatormittens";
131         static const uint32_t uint = 0xb00bface;
132         static const uint32_t bitflags = 0x12488421;
133
134         switch (cmd) {
135         case TEST_INIT:
136                 info->name = "ast_event_new_test";
137                 info->category = "main/event/";
138                 info->summary = "Test event creation";
139                 info->description =
140                         "This test exercises the API calls that allow allocation "
141                         "of an ast_event.";
142                 return AST_TEST_NOT_RUN;
143         case TEST_EXECUTE:
144                 break;
145         }
146
147         /*
148          * Test 2 methods of event creation:
149          *
150          * 1) Dynamic via appending each IE individually.
151          * 2) Statically, with all IEs in ast_event_new().
152          */
153
154         ast_test_status_update(test, "First, test dynamic event creation...\n");
155
156         if (!(event = ast_event_new(type, AST_EVENT_IE_END))) {
157                 ast_test_status_update(test, "Failed to allocate ast_event object.\n");
158                 res = AST_TEST_FAIL;
159                 goto return_cleanup;
160         }
161
162         if (ast_event_append_ie_str(&event, AST_EVENT_IE_MAILBOX, str)) {
163                 ast_test_status_update(test, "Failed to append str IE\n");
164                 res = AST_TEST_FAIL;
165                 goto return_cleanup;
166         }
167
168         if (ast_event_append_ie_uint(&event, AST_EVENT_IE_NEWMSGS, uint)) {
169                 ast_test_status_update(test, "Failed to append uint IE\n");
170                 res = AST_TEST_FAIL;
171                 goto return_cleanup;
172         }
173
174         if (ast_event_append_ie_bitflags(&event, AST_EVENT_IE_OLDMSGS, bitflags)) {
175                 ast_test_status_update(test, "Failed to append bitflags IE\n");
176                 res = AST_TEST_FAIL;
177                 goto return_cleanup;
178         }
179
180         if (ast_event_append_eid(&event)) {
181                 ast_test_status_update(test, "Failed to append EID\n");
182                 res = AST_TEST_FAIL;
183                 goto return_cleanup;
184         }
185
186         if (check_event(event, test, type, "Custom", str, uint, bitflags)) {
187                 ast_test_status_update(test, "Dynamically generated event broken\n");
188                 res = AST_TEST_FAIL;
189                 goto return_cleanup;
190         }
191
192         event2 = ast_event_new(type,
193                         AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, str,
194                         AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, uint,
195                         AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_BITFLAGS, bitflags,
196                         AST_EVENT_IE_END);
197
198         if (!event2) {
199                 ast_test_status_update(test, "Failed to allocate ast_event object.\n");
200                 res = AST_TEST_FAIL;
201                 goto return_cleanup;
202         }
203
204         if (check_event(event2, test, type, "Custom", str, uint, bitflags)) {
205                 ast_test_status_update(test, "Statically generated event broken\n");
206                 res = AST_TEST_FAIL;
207                 goto return_cleanup;
208         }
209
210         if (ast_event_get_size(event) != ast_event_get_size(event2)) {
211                 ast_test_status_update(test, "Events expected to be identical have different size: %d != %d\n",
212                                 (int) ast_event_get_size(event),
213                                 (int) ast_event_get_size(event2));
214                 res = AST_TEST_FAIL;
215                 goto return_cleanup;
216         }
217
218 return_cleanup:
219         if (event) {
220                 ast_event_destroy(event);
221                 event = NULL;
222         }
223
224         if (event2) {
225                 ast_event_destroy(event2);
226                 event2 = NULL;
227         }
228
229         return res;
230 }
231
232 struct event_sub_data {
233         unsigned int count;
234 };
235
236 static void event_sub_cb(const struct ast_event *event, void *d)
237 {
238         struct event_sub_data *data = d;
239
240         data->count++;
241 }
242
243 /*!
244  * \internal
245  * \brief Test event subscriptions
246  *
247  * - Query for existing Subscriptions:
248  *   - ast_event_check_subscriber()
249  */
250 AST_TEST_DEFINE(event_sub_test)
251 {
252         enum ast_test_result_state res = AST_TEST_PASS;
253         struct ast_event *event;
254         int i;
255         enum ast_event_subscriber_res sub_res;
256         static struct {
257                 struct ast_event_sub *sub;
258                 struct event_sub_data data;
259                 const unsigned int expected_count;
260         } test_subs[] = {
261                 [0] = {
262                         .expected_count = 3,
263                 },
264                 [1] = {
265                         .expected_count = 1,
266                 },
267                 [2] = {
268                         .expected_count = 2,
269                 },
270         };
271
272         switch (cmd) {
273         case TEST_INIT:
274                 info->name = "ast_event_subscribe_test";
275                 info->category = "main/event/";
276                 info->summary = "Test event subscriptions";
277                 info->description =
278                         "This test exercises the API calls that allow subscriptions "
279                         "to events.";
280                 return AST_TEST_NOT_RUN;
281         case TEST_EXECUTE:
282                 break;
283         }
284
285         /*
286          * Subscription #1:
287          *  - allocate normally
288          *  - subscribe to all CUSTOM events
289          *
290          * Subscription #2:
291          *  - allocate dynamically
292          *  - subscribe to all CUSTOM events
293          *  - add payload checks
294          *
295          * Subscription #3:
296          *  - allocate normally
297          *  - subscribe to all events with an IE check
298          */
299
300         test_subs[0].sub = ast_event_subscribe(AST_EVENT_CUSTOM, event_sub_cb, "test_sub", &test_subs[0].data,
301                         AST_EVENT_IE_END);
302         if (!test_subs[0].sub) {
303                 ast_test_status_update(test, "Failed to create test_subs[0].sub\n");
304                 res = AST_TEST_FAIL;
305                 goto return_cleanup;
306         }
307
308         if (strcmp(ast_event_subscriber_get_description(test_subs[0].sub), "test_sub")) {
309                 ast_test_status_update(test,
310                                 "Unexpected subscription description on test_subs[0].sub\n");
311                 res = AST_TEST_FAIL;
312                 goto return_cleanup;
313         }
314
315         test_subs[1].sub = ast_event_subscribe_new(AST_EVENT_CUSTOM, event_sub_cb, &test_subs[1].data);
316         if (!test_subs[1].sub) {
317                 ast_test_status_update(test, "Failed to create test_subs[1].sub\n");
318                 res = AST_TEST_FAIL;
319                 goto return_cleanup;
320         }
321
322         /* For the sake of exercising destruction before activation */
323         ast_event_sub_destroy(test_subs[1].sub);
324
325         test_subs[1].sub = ast_event_subscribe_new(AST_EVENT_CUSTOM, event_sub_cb, &test_subs[1].data);
326         if (!test_subs[1].sub) {
327                 ast_test_status_update(test, "Failed to create test_subs[1].sub\n");
328                 res = AST_TEST_FAIL;
329                 goto return_cleanup;
330         }
331
332         if (strcmp(ast_event_subscriber_get_description(test_subs[1].sub), "")) {
333                 ast_event_sub_destroy(test_subs[1].sub);
334                 test_subs[1].sub = NULL;
335                 ast_test_status_update(test,
336                                 "Unexpected subscription description on test_subs[1].sub\n");
337                 res = AST_TEST_FAIL;
338                 goto return_cleanup;
339         }
340
341         if (ast_event_sub_append_ie_uint(test_subs[1].sub, AST_EVENT_IE_NEWMSGS, 3)) {
342                 ast_event_sub_destroy(test_subs[1].sub);
343                 test_subs[1].sub = NULL;
344                 ast_test_status_update(test, "Failed to append uint IE to test_subs[1].sub\n");
345                 res = AST_TEST_FAIL;
346                 goto return_cleanup;
347         }
348
349         if (ast_event_sub_append_ie_bitflags(test_subs[1].sub, AST_EVENT_IE_NEWMSGS, 1)) {
350                 ast_event_sub_destroy(test_subs[1].sub);
351                 test_subs[1].sub = NULL;
352                 ast_test_status_update(test, "Failed to append bitflags IE to test_subs[1].sub\n");
353                 res = AST_TEST_FAIL;
354                 goto return_cleanup;
355         }
356
357         if (ast_event_sub_append_ie_str(test_subs[1].sub, AST_EVENT_IE_DEVICE, "FOO/bar")) {
358                 ast_event_sub_destroy(test_subs[1].sub);
359                 test_subs[1].sub = NULL;
360                 ast_test_status_update(test, "Failed to append str IE to test_subs[1].sub\n");
361                 res = AST_TEST_FAIL;
362                 goto return_cleanup;
363         }
364
365         if (ast_event_sub_append_ie_raw(test_subs[1].sub, AST_EVENT_IE_MAILBOX, "800 km",
366                         strlen("800 km"))) {
367                 ast_event_sub_destroy(test_subs[1].sub);
368                 test_subs[1].sub = NULL;
369                 ast_test_status_update(test, "Failed to append raw IE to test_subs[1].sub\n");
370                 res = AST_TEST_FAIL;
371                 goto return_cleanup;
372         }
373
374         if (ast_event_sub_append_ie_exists(test_subs[1].sub, AST_EVENT_IE_DEVICE)) {
375                 ast_event_sub_destroy(test_subs[1].sub);
376                 test_subs[1].sub = NULL;
377                 ast_test_status_update(test, "Failed to append exists IE to test_subs[1].sub\n");
378                 res = AST_TEST_FAIL;
379                 goto return_cleanup;
380         }
381
382         if (ast_event_sub_activate(test_subs[1].sub)) {
383                 ast_event_sub_destroy(test_subs[1].sub);
384                 test_subs[1].sub = NULL;
385                 ast_test_status_update(test, "Failed to activate test_subs[1].sub\n");
386                 res = AST_TEST_FAIL;
387                 goto return_cleanup;
388         }
389
390         test_subs[2].sub = ast_event_subscribe(AST_EVENT_ALL, event_sub_cb, "test_sub", &test_subs[2].data,
391                         AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "FOO/bar",
392                         AST_EVENT_IE_END);
393         if (!test_subs[2].sub) {
394                 ast_test_status_update(test, "Failed to create test_subs[2].sub\n");
395                 res = AST_TEST_FAIL;
396                 goto return_cleanup;
397         }
398
399         /*
400          * Exercise the API call to check for existing subscriptions.
401          */
402
403         sub_res = ast_event_check_subscriber(AST_EVENT_CUSTOM,
404                         AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "FOO/bar",
405                         AST_EVENT_IE_END);
406         if (sub_res != AST_EVENT_SUB_EXISTS) {
407                 ast_test_status_update(test, "subscription did not exist\n");
408                 res = AST_TEST_FAIL;
409                 goto return_cleanup;
410         }
411
412         sub_res = ast_event_check_subscriber(AST_EVENT_CUSTOM,
413                         AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "BOOBS",
414                         AST_EVENT_IE_END);
415         if (sub_res != AST_EVENT_SUB_NONE) {
416                 ast_test_status_update(test, "Someone subscribed to updates on boobs, lol? (%d)\n", sub_res);
417                 res = AST_TEST_FAIL;
418                 goto return_cleanup;
419         }
420
421         /*
422          * Fire off some events and track what was received in the callback
423          *
424          * event #1:
425          *  - simple custom event (will match sub 1 and 3)
426          *
427          * event #2:
428          *  - custom event with payloads that satisfy every payload check
429          *    for sub #2 (will match sub 1, 2, and 3)
430          *
431          * event #3:
432          *  - custom event that should only match sub #1
433          */
434
435         event = ast_event_new(AST_EVENT_CUSTOM,
436                         AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "FOO/bar",
437                         AST_EVENT_IE_END);
438         if (!event) {
439                 ast_test_status_update(test, "Failed to create event\n");
440                 res = AST_TEST_FAIL;
441                 goto return_cleanup;
442         }
443         if (ast_event_queue(event)) {
444                 ast_event_destroy(event);
445                 event = NULL;
446                 ast_test_status_update(test, "Failed to queue event\n");
447                 res = AST_TEST_FAIL;
448                 goto return_cleanup;
449         }
450
451         event = ast_event_new(AST_EVENT_CUSTOM,
452                         AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "FOO/bar",
453                         AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_RAW, "800 km", strlen("800 km"),
454                         AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, 3,
455                         AST_EVENT_IE_END);
456         if (!event) {
457                 ast_test_status_update(test, "Failed to create event\n");
458                 res = AST_TEST_FAIL;
459                 goto return_cleanup;
460         }
461         if (ast_event_queue(event)) {
462                 ast_event_destroy(event);
463                 event = NULL;
464                 ast_test_status_update(test, "Failed to queue event\n");
465                 res = AST_TEST_FAIL;
466                 goto return_cleanup;
467         }
468
469         event = ast_event_new(AST_EVENT_CUSTOM,
470                         AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "blah",
471                         AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_RAW, "801 km", strlen("801 km"),
472                         AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, 0,
473                         AST_EVENT_IE_END);
474         if (!event) {
475                 ast_test_status_update(test, "Failed to create event\n");
476                 res = AST_TEST_FAIL;
477                 goto return_cleanup;
478         }
479         if (ast_event_queue(event)) {
480                 ast_event_destroy(event);
481                 event = NULL;
482                 ast_test_status_update(test, "Failed to queue event\n");
483                 res = AST_TEST_FAIL;
484                 goto return_cleanup;
485         }
486         event = NULL;
487
488         /*
489          * Check the results of the test.
490          *
491          * First of all, event distribution is asynchronous from the event producer,
492          * so knowing when to continue from here and check results is an instance of
493          * the halting problem.  A few seconds really should be more than enough time.
494          * If something was actually blocking event distribution that long, I would call
495          * it a bug.
496          *
497          * Expected results:
498          *  - sub 1, 2 events
499          *  - sub 2, 1 event
500          *  - sub 3, 2 events
501          */
502
503         ast_test_status_update(test, "Sleeping a few seconds to allow event propagation...\n");
504         sleep(3);
505
506         for (i = 0; i < ARRAY_LEN(test_subs); i++) {
507                 if (test_subs[i].data.count != test_subs[i].expected_count) {
508                         ast_test_status_update(test, "Unexpected callback count, %u != %u for #%d\n",
509                                         test_subs[i].data.count, test_subs[i].expected_count, i);
510                         res = AST_TEST_FAIL;
511                 }
512         }
513
514 return_cleanup:
515         for (i = 0; i < ARRAY_LEN(test_subs); i++) {
516                 if (test_subs[i].sub) {
517                         test_subs[i].sub = ast_event_unsubscribe(test_subs[i].sub);
518                 }
519         }
520
521         return res;
522 }
523
524 static int unload_module(void)
525 {
526         AST_TEST_UNREGISTER(event_new_test);
527         AST_TEST_UNREGISTER(event_sub_test);
528
529         return 0;
530 }
531
532 static int load_module(void)
533 {
534         AST_TEST_REGISTER(event_new_test);
535         AST_TEST_REGISTER(event_sub_test);
536
537         return AST_MODULE_LOAD_SUCCESS;
538 }
539
540 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ast_event API Tests");