git migration: Refactor the ASTERISK_FILE_VERSION macro
[asterisk/asterisk.git] / tests / test_devicestate.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2010, Digium, Inc.
5  *
6  * David Vossel <dvossel@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 Device State Test Module
22  *
23  * \author David Vossel <dvossel@digium.com>
24  *
25  * \ingroup tests
26  */
27
28 /*** MODULEINFO
29         <depend>TEST_FRAMEWORK</depend>
30         <support_level>core</support_level>
31  ***/
32
33 #include "asterisk.h"
34
35 ASTERISK_REGISTER_FILE()
36
37 #include "asterisk/utils.h"
38 #include "asterisk/module.h"
39 #include "asterisk/test.h"
40 #include "asterisk/devicestate.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/stasis_message_router.h"
43
44 #define UNIT_TEST_DEVICE_IDENTIFIER "unit_test_device_identifier"
45
46 /* These arrays are the result of the 'core show device2extenstate' output. */
47 static int combined_results[] = {
48         AST_DEVICE_UNKNOWN,
49         AST_DEVICE_NOT_INUSE,
50         AST_DEVICE_INUSE,
51         AST_DEVICE_BUSY,
52         AST_DEVICE_UNKNOWN,
53         AST_DEVICE_UNAVAILABLE,
54         AST_DEVICE_RINGING,
55         AST_DEVICE_RINGINUSE,
56         AST_DEVICE_ONHOLD,
57         AST_DEVICE_NOT_INUSE,
58         AST_DEVICE_NOT_INUSE,
59         AST_DEVICE_INUSE,
60         AST_DEVICE_BUSY,
61         AST_DEVICE_NOT_INUSE,
62         AST_DEVICE_NOT_INUSE,
63         AST_DEVICE_RINGING,
64         AST_DEVICE_RINGINUSE,
65         AST_DEVICE_ONHOLD,
66         AST_DEVICE_INUSE,
67         AST_DEVICE_INUSE,
68         AST_DEVICE_INUSE,
69         AST_DEVICE_BUSY,
70         AST_DEVICE_INUSE,
71         AST_DEVICE_INUSE,
72         AST_DEVICE_RINGINUSE,
73         AST_DEVICE_RINGINUSE,
74         AST_DEVICE_INUSE,
75         AST_DEVICE_BUSY,
76         AST_DEVICE_BUSY,
77         AST_DEVICE_BUSY,
78         AST_DEVICE_BUSY,
79         AST_DEVICE_BUSY,
80         AST_DEVICE_BUSY,
81         AST_DEVICE_RINGINUSE,
82         AST_DEVICE_RINGINUSE,
83         AST_DEVICE_BUSY,
84         AST_DEVICE_UNKNOWN,
85         AST_DEVICE_NOT_INUSE,
86         AST_DEVICE_INUSE,
87         AST_DEVICE_BUSY,
88         AST_DEVICE_INVALID,
89         AST_DEVICE_UNAVAILABLE,
90         AST_DEVICE_RINGING,
91         AST_DEVICE_RINGINUSE,
92         AST_DEVICE_ONHOLD,
93         AST_DEVICE_UNAVAILABLE,
94         AST_DEVICE_NOT_INUSE,
95         AST_DEVICE_INUSE,
96         AST_DEVICE_BUSY,
97         AST_DEVICE_UNAVAILABLE,
98         AST_DEVICE_UNAVAILABLE,
99         AST_DEVICE_RINGING,
100         AST_DEVICE_RINGINUSE,
101         AST_DEVICE_ONHOLD,
102         AST_DEVICE_RINGING,
103         AST_DEVICE_RINGING,
104         AST_DEVICE_RINGINUSE,
105         AST_DEVICE_RINGINUSE,
106         AST_DEVICE_RINGING,
107         AST_DEVICE_RINGING,
108         AST_DEVICE_RINGING,
109         AST_DEVICE_RINGINUSE,
110         AST_DEVICE_RINGINUSE,
111         AST_DEVICE_RINGINUSE,
112         AST_DEVICE_RINGINUSE,
113         AST_DEVICE_RINGINUSE,
114         AST_DEVICE_RINGINUSE,
115         AST_DEVICE_RINGINUSE,
116         AST_DEVICE_RINGINUSE,
117         AST_DEVICE_RINGINUSE,
118         AST_DEVICE_RINGINUSE,
119         AST_DEVICE_RINGINUSE,
120         AST_DEVICE_ONHOLD,
121         AST_DEVICE_ONHOLD,
122         AST_DEVICE_INUSE,
123         AST_DEVICE_BUSY,
124         AST_DEVICE_ONHOLD,
125         AST_DEVICE_ONHOLD,
126         AST_DEVICE_RINGINUSE,
127         AST_DEVICE_RINGINUSE,
128         AST_DEVICE_ONHOLD,
129 };
130
131 static int exten_results[] = {
132         AST_EXTENSION_NOT_INUSE,
133         AST_EXTENSION_NOT_INUSE,
134         AST_EXTENSION_INUSE,
135         AST_EXTENSION_BUSY,
136         AST_EXTENSION_NOT_INUSE,
137         AST_EXTENSION_UNAVAILABLE,
138         AST_EXTENSION_RINGING,
139         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
140         AST_EXTENSION_ONHOLD,
141         AST_EXTENSION_NOT_INUSE,
142         AST_EXTENSION_NOT_INUSE,
143         AST_EXTENSION_INUSE,
144         AST_EXTENSION_BUSY,
145         AST_EXTENSION_NOT_INUSE,
146         AST_EXTENSION_NOT_INUSE,
147         AST_EXTENSION_RINGING,
148         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
149         AST_EXTENSION_ONHOLD,
150         AST_EXTENSION_INUSE,
151         AST_EXTENSION_INUSE,
152         AST_EXTENSION_INUSE,
153         AST_EXTENSION_BUSY,
154         AST_EXTENSION_INUSE,
155         AST_EXTENSION_INUSE,
156         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
157         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
158         AST_EXTENSION_INUSE,
159         AST_EXTENSION_BUSY,
160         AST_EXTENSION_BUSY,
161         AST_EXTENSION_BUSY,
162         AST_EXTENSION_BUSY,
163         AST_EXTENSION_BUSY,
164         AST_EXTENSION_BUSY,
165         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
166         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
167         AST_EXTENSION_BUSY,
168         AST_EXTENSION_NOT_INUSE,
169         AST_EXTENSION_NOT_INUSE,
170         AST_EXTENSION_INUSE,
171         AST_EXTENSION_BUSY,
172         AST_EXTENSION_UNAVAILABLE,
173         AST_EXTENSION_UNAVAILABLE,
174         AST_EXTENSION_RINGING,
175         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
176         AST_EXTENSION_ONHOLD,
177         AST_EXTENSION_UNAVAILABLE,
178         AST_EXTENSION_NOT_INUSE,
179         AST_EXTENSION_INUSE,
180         AST_EXTENSION_BUSY,
181         AST_EXTENSION_UNAVAILABLE,
182         AST_EXTENSION_UNAVAILABLE,
183         AST_EXTENSION_RINGING,
184         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
185         AST_EXTENSION_ONHOLD,
186         AST_EXTENSION_RINGING,
187         AST_EXTENSION_RINGING,
188         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
189         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
190         AST_EXTENSION_RINGING,
191         AST_EXTENSION_RINGING,
192         AST_EXTENSION_RINGING,
193         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
194         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
195         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
196         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
197         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
198         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
199         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
200         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
201         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
202         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
203         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
204         AST_EXTENSION_ONHOLD,
205         AST_EXTENSION_ONHOLD,
206         AST_EXTENSION_INUSE,
207         AST_EXTENSION_BUSY,
208         AST_EXTENSION_ONHOLD,
209         AST_EXTENSION_ONHOLD,
210         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
211         AST_EXTENSION_INUSE | AST_EXTENSION_RINGING,
212         AST_EXTENSION_ONHOLD,
213 };
214
215 AST_TEST_DEFINE(device2extenstate_test)
216 {
217         int res = AST_TEST_PASS;
218         struct ast_devstate_aggregate agg;
219         enum ast_device_state i, j, combined;
220         enum ast_extension_states exten;
221         int k = 0;
222
223         switch (cmd) {
224         case TEST_INIT:
225                 info->name = "device2extenstate_test";
226                 info->category = "/main/devicestate/";
227                 info->summary = "Tests combined devstate mapping and device to extension state mapping.";
228                 info->description =
229                         "Verifies device state aggregate results match the expected combined "
230                         "devstate.  Then verifies the combined devstate maps to the expected "
231                         "extension state.";
232                 return AST_TEST_NOT_RUN;
233         case TEST_EXECUTE:
234                 break;
235         }
236
237         if (ARRAY_LEN(exten_results) != (AST_DEVICE_TOTAL * AST_DEVICE_TOTAL)) {
238                 ast_test_status_update(test, "Result array is %d long when it should be %d. "
239                         "Something has changed, this test must be updated.\n",
240                         (int) ARRAY_LEN(exten_results), (AST_DEVICE_TOTAL * AST_DEVICE_TOTAL));
241                 return AST_TEST_FAIL;
242         }
243
244         if (ARRAY_LEN(combined_results) != ARRAY_LEN(exten_results)) {
245                 ast_test_status_update(test, "combined_results and exten_results arrays do not match in length.\n");
246                 return AST_TEST_FAIL;
247         }
248
249         for (i = 0; i < AST_DEVICE_TOTAL; i++) {
250                 for (j = 0; j < AST_DEVICE_TOTAL; j++) {
251                         ast_devstate_aggregate_init(&agg);
252                         ast_devstate_aggregate_add(&agg, i);
253                         ast_devstate_aggregate_add(&agg, j);
254                         combined = ast_devstate_aggregate_result(&agg);
255                         if (combined_results[k] != combined) {
256                                 ast_test_status_update(test, "Expected combined dev state %s "
257                                         "does not match %s at combined_result[%d].\n",
258                                         ast_devstate2str(combined_results[k]),
259                                         ast_devstate2str(combined), k);
260                                 res = AST_TEST_FAIL;
261                         }
262
263                         exten = ast_devstate_to_extenstate(combined);
264
265                         if (exten_results[k] != exten) {
266                                 ast_test_status_update(test, "Expected exten state %s "
267                                         "does not match %s at exten_result[%d]\n",
268                                         ast_extension_state2str(exten_results[k]),
269                                         ast_extension_state2str(exten), k);
270                                 res = AST_TEST_FAIL;
271                         }
272                         k++;
273                 }
274         }
275
276         return res;
277 }
278
279 struct consumer {
280         ast_cond_t out;
281         int already_out;
282         int sig_on_non_aggregate_state;
283         int event_count;
284         enum ast_device_state state;
285         enum ast_device_state aggregate_state;
286 };
287
288 static void consumer_dtor(void *obj)
289 {
290         struct consumer *consumer = obj;
291
292         ast_cond_destroy(&consumer->out);
293 }
294
295 static void consumer_reset(struct consumer *consumer)
296 {
297         consumer->already_out = 0;
298         consumer->event_count = 0;
299         consumer->state = AST_DEVICE_TOTAL;
300         consumer->aggregate_state = AST_DEVICE_TOTAL;
301 }
302
303 static struct consumer *consumer_create(void)
304 {
305         struct consumer *consumer;
306
307         consumer = ao2_alloc(sizeof(*consumer), consumer_dtor);
308         if (!consumer) {
309                 return NULL;
310         }
311
312         ast_cond_init(&consumer->out, NULL);
313         consumer_reset(consumer);
314
315         return consumer;
316 }
317
318 static void consumer_exec(void *data, struct stasis_subscription *sub, struct stasis_message *message)
319 {
320         struct consumer *consumer = data;
321         struct stasis_cache_update *cache_update = stasis_message_data(message);
322         struct ast_device_state_message *device_state;
323
324         if (!cache_update->new_snapshot) {
325                 return;
326         }
327
328         device_state = stasis_message_data(cache_update->new_snapshot);
329
330         if (strcmp(device_state->device, UNIT_TEST_DEVICE_IDENTIFIER)) {
331                 /* not a device state we're interested in */
332                 return;
333         }
334
335         {
336                 SCOPED_AO2LOCK(lock, consumer);
337
338                 ++consumer->event_count;
339                 if (device_state->eid) {
340                         consumer->state = device_state->state;
341                         if (consumer->sig_on_non_aggregate_state) {
342                                 consumer->sig_on_non_aggregate_state = 0;
343                                 consumer->already_out = 1;
344                                 ast_cond_signal(&consumer->out);
345                         }
346                 } else {
347                         consumer->aggregate_state = device_state->state;
348                         consumer->already_out = 1;
349                         ast_cond_signal(&consumer->out);
350                 }
351         }
352 }
353
354 static void consumer_finalize(void *data, struct stasis_subscription *sub, struct stasis_message *message)
355 {
356         struct consumer *consumer = data;
357
358         if (stasis_subscription_final_message(sub, message)) {
359                 ao2_cleanup(consumer);
360         }
361 }
362
363 static void consumer_wait_for(struct consumer *consumer)
364 {
365         int res;
366         struct timeval start = ast_tvnow();
367         struct timespec end = {
368                 .tv_sec = start.tv_sec + 10,
369                 .tv_nsec = start.tv_usec * 1000
370         };
371
372         SCOPED_AO2LOCK(lock, consumer);
373
374         while (!consumer->already_out) {
375                 res = ast_cond_timedwait(&consumer->out, ao2_object_get_lockaddr(consumer), &end);
376                 if (!res || res == ETIMEDOUT) {
377                         break;
378                 }
379         }
380 }
381
382 static int remove_device_states_cb(void *obj, void *arg, int flags)
383 {
384         struct stasis_message *msg = obj;
385         struct ast_device_state_message *device_state = stasis_message_data(msg);
386
387         if (strcmp(UNIT_TEST_DEVICE_IDENTIFIER, device_state->device)) {
388                 /* Not a unit test device */
389                 return 0;
390         }
391
392         msg = stasis_cache_clear_create(msg);
393         if (msg) {
394                 /* topic guaranteed to have been created by this point */
395                 stasis_publish(ast_device_state_topic(device_state->device), msg);
396         }
397         ao2_cleanup(msg);
398         return 0;
399 }
400
401 static void cache_cleanup(int unused)
402 {
403         struct ao2_container *cache_dump;
404
405         /* remove all device states created during this test */
406         cache_dump = stasis_cache_dump_all(ast_device_state_cache(), NULL);
407         if (!cache_dump) {
408                 return;
409         }
410         ao2_callback(cache_dump, 0, remove_device_states_cb, NULL);
411         ao2_cleanup(cache_dump);
412 }
413
414 AST_TEST_DEFINE(device_state_aggregation_test)
415 {
416         RAII_VAR(struct consumer *, consumer, NULL, ao2_cleanup);
417         RAII_VAR(struct stasis_message_router *, device_msg_router, NULL, stasis_message_router_unsubscribe);
418         RAII_VAR(struct ast_eid *, foreign_eid, NULL, ast_free);
419         RAII_VAR(int, cleanup_cache, 0, cache_cleanup);
420         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
421         int res;
422         struct ast_device_state_message *device_state;
423
424         switch (cmd) {
425         case TEST_INIT:
426                 info->name = "device_state_aggregation_test";
427                 info->category = "/main/devicestate/";
428                 info->summary = "Tests message routing and aggregation through the Stasis device state system.";
429                 info->description =
430                         "Verifies that the device state system passes "
431                         "messages appropriately, that the aggregator is "
432                         "working properly, that the aggregate results match "
433                         "the expected combined devstate, and that the cached "
434                         "aggregate devstate is correct.";
435                 return AST_TEST_NOT_RUN;
436         case TEST_EXECUTE:
437                 break;
438         }
439
440         foreign_eid = ast_malloc(sizeof(*foreign_eid));
441         ast_test_validate(test, NULL != foreign_eid);
442         memset(foreign_eid, 0xFF, sizeof(*foreign_eid));
443
444         consumer = consumer_create();
445         ast_test_validate(test, NULL != consumer);
446
447         device_msg_router = stasis_message_router_create(ast_device_state_topic_cached());
448         ast_test_validate(test, NULL != device_msg_router);
449
450         ao2_ref(consumer, +1);
451         res = stasis_message_router_add(device_msg_router, stasis_cache_update_type(), consumer_exec, consumer);
452         ast_test_validate(test, !res);
453
454         res = stasis_message_router_add(device_msg_router, stasis_subscription_change_type(), consumer_finalize, consumer);
455         ast_test_validate(test, !res);
456
457         /* push local state */
458         ast_publish_device_state(UNIT_TEST_DEVICE_IDENTIFIER, AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE);
459
460         /* Check cache aggregate state immediately */
461         ao2_cleanup(msg);
462         msg = stasis_cache_get_by_eid(ast_device_state_cache(), ast_device_state_message_type(), UNIT_TEST_DEVICE_IDENTIFIER, NULL);
463         device_state = stasis_message_data(msg);
464         ast_test_validate(test, AST_DEVICE_NOT_INUSE == device_state->state);
465
466         consumer_wait_for(consumer);
467         ast_test_validate(test, AST_DEVICE_NOT_INUSE == consumer->state);
468         ast_test_validate(test, AST_DEVICE_NOT_INUSE == consumer->aggregate_state);
469         ast_test_validate(test, 2 == consumer->event_count);
470         consumer_reset(consumer);
471
472         /* push remote state */
473         /* this will not produce a new aggregate state message since the aggregate state does not change */
474         consumer->sig_on_non_aggregate_state = 1;
475         ast_publish_device_state_full(UNIT_TEST_DEVICE_IDENTIFIER, AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, foreign_eid);
476
477         /* Check cache aggregate state immediately */
478         ao2_cleanup(msg);
479         msg = stasis_cache_get_by_eid(ast_device_state_cache(), ast_device_state_message_type(), UNIT_TEST_DEVICE_IDENTIFIER, NULL);
480         device_state = stasis_message_data(msg);
481         ast_test_validate(test, AST_DEVICE_NOT_INUSE == device_state->state);
482
483         /* Check for expected events. */
484         consumer_wait_for(consumer);
485         ast_test_validate(test, AST_DEVICE_NOT_INUSE == consumer->state);
486         ast_test_validate(test, AST_DEVICE_TOTAL == consumer->aggregate_state);
487         ast_test_validate(test, 1 == consumer->event_count);
488         consumer_reset(consumer);
489
490         /* push remote state different from local state */
491         ast_publish_device_state_full(UNIT_TEST_DEVICE_IDENTIFIER, AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, foreign_eid);
492
493         /* Check cache aggregate state immediately */
494         ao2_cleanup(msg);
495         msg = stasis_cache_get_by_eid(ast_device_state_cache(), ast_device_state_message_type(), UNIT_TEST_DEVICE_IDENTIFIER, NULL);
496         device_state = stasis_message_data(msg);
497         ast_test_validate(test, AST_DEVICE_INUSE == device_state->state);
498
499         /* Check for expected events. */
500         consumer_wait_for(consumer);
501         ast_test_validate(test, AST_DEVICE_INUSE == consumer->state);
502         ast_test_validate(test, AST_DEVICE_INUSE == consumer->aggregate_state);
503         ast_test_validate(test, 2 == consumer->event_count);
504         consumer_reset(consumer);
505
506         /* push local state that will cause aggregated state different from local non-aggregate state */
507         ast_publish_device_state(UNIT_TEST_DEVICE_IDENTIFIER, AST_DEVICE_RINGING, AST_DEVSTATE_CACHABLE);
508
509         /* Check cache aggregate state immediately */
510         ao2_cleanup(msg);
511         msg = stasis_cache_get_by_eid(ast_device_state_cache(), ast_device_state_message_type(), UNIT_TEST_DEVICE_IDENTIFIER, NULL);
512         device_state = stasis_message_data(msg);
513         ast_test_validate(test, AST_DEVICE_RINGINUSE == device_state->state);
514
515         /* Check for expected events. */
516         consumer_wait_for(consumer);
517         ast_test_validate(test, AST_DEVICE_RINGING == consumer->state);
518         ast_test_validate(test, AST_DEVICE_RINGINUSE == consumer->aggregate_state);
519         ast_test_validate(test, 2 == consumer->event_count);
520         consumer_reset(consumer);
521
522         return AST_TEST_PASS;
523 }
524
525 static int unload_module(void)
526 {
527         AST_TEST_UNREGISTER(device2extenstate_test);
528         AST_TEST_UNREGISTER(device_state_aggregation_test);
529         return 0;
530 }
531
532 static int load_module(void)
533 {
534         AST_TEST_REGISTER(device_state_aggregation_test);
535         AST_TEST_REGISTER(device2extenstate_test);
536         return AST_MODULE_LOAD_SUCCESS;
537 }
538
539 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Device State Test");