Multiple revisions 399887,400138,400178,400180-400181
[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_FILE_VERSION(__FILE__, "$Revision$")
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_mutex_t lock;
281         ast_cond_t out;
282         int already_out;
283         enum ast_device_state state;
284         enum ast_device_state aggregate_state;
285         int sig_on_non_aggregate_state;
286 };
287
288 static void consumer_dtor(void *obj) {
289         struct consumer *consumer = obj;
290
291         ast_mutex_destroy(&consumer->lock);
292         ast_cond_destroy(&consumer->out);
293 }
294
295 static struct consumer *consumer_create(void) {
296         RAII_VAR(struct consumer *, consumer, NULL, ao2_cleanup);
297
298         consumer = ao2_alloc(sizeof(*consumer), consumer_dtor);
299
300         if (!consumer) {
301                 return NULL;
302         }
303
304         ast_mutex_init(&consumer->lock);
305         ast_cond_init(&consumer->out, NULL);
306         consumer->sig_on_non_aggregate_state = 0;
307
308         ao2_ref(consumer, +1);
309         return consumer;
310 }
311
312 static void consumer_exec(void *data, struct stasis_subscription *sub, struct stasis_message *message)
313 {
314         struct consumer *consumer = data;
315         RAII_VAR(struct consumer *, consumer_needs_cleanup, NULL, ao2_cleanup);
316         struct stasis_cache_update *cache_update = stasis_message_data(message);
317         struct ast_device_state_message *device_state;
318         SCOPED_MUTEX(lock, &consumer->lock);
319
320         if (!cache_update->new_snapshot) {
321                 return;
322         }
323
324         device_state = stasis_message_data(cache_update->new_snapshot);
325
326         if (strcmp(device_state->device, UNIT_TEST_DEVICE_IDENTIFIER)) {
327                 /* not a device state we're interested in */
328                 return;
329         }
330
331         if (device_state->eid) {
332                 consumer->state = device_state->state;
333                 if (consumer->sig_on_non_aggregate_state) {
334                         consumer->sig_on_non_aggregate_state = 0;
335                         consumer->already_out = 1;
336                         ast_cond_signal(&consumer->out);
337                 }
338         } else {
339                 consumer->aggregate_state = device_state->state;
340                 consumer->already_out = 1;
341                 ast_cond_signal(&consumer->out);
342         }
343 }
344
345 static void consumer_finalize(void *data, struct stasis_subscription *sub, struct stasis_message *message)
346 {
347         struct consumer *consumer = data;
348
349         if (stasis_subscription_final_message(sub, message)) {
350                 ao2_cleanup(consumer);
351         }
352 }
353
354 static void consumer_wait_for(struct consumer *consumer)
355 {
356         int res;
357         struct timeval start = ast_tvnow();
358         struct timespec end = {
359                 .tv_sec = start.tv_sec + 10,
360                 .tv_nsec = start.tv_usec * 1000
361         };
362
363         SCOPED_MUTEX(lock, &consumer->lock);
364
365         if (consumer->already_out) {
366                 consumer->already_out = 0;
367         }
368
369         while(1) {
370                 res = ast_cond_timedwait(&consumer->out, &consumer->lock, &end);
371                 if (!res || res == ETIMEDOUT) {
372                         break;
373                 }
374         }
375         consumer->already_out = 0;
376 }
377
378 static int remove_device_states_cb(void *obj, void *arg, int flags)
379 {
380         RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup);
381         struct ast_device_state_message *device_state = stasis_message_data(msg);
382         if (strcmp(UNIT_TEST_DEVICE_IDENTIFIER, device_state->device)) {
383                 msg = NULL;
384                 return 0;
385         }
386
387         msg = stasis_cache_clear_create(msg);
388         /* topic guaranteed to have been created by this point */
389         stasis_publish(ast_device_state_topic(device_state->device), msg);
390         return 0;
391 }
392
393 static void cache_cleanup(int unused)
394 {
395         RAII_VAR(struct ao2_container *, cache_dump, NULL, ao2_cleanup);
396         /* remove all device states created during this test */
397         cache_dump = stasis_cache_dump(ast_device_state_cache(), NULL);
398         if (!cache_dump) {
399                 return;
400         }
401         ao2_callback(cache_dump, 0, remove_device_states_cb, NULL);
402 }
403
404 AST_TEST_DEFINE(device_state_aggregation_test)
405 {
406         RAII_VAR(struct consumer *, consumer, NULL, ao2_cleanup);
407         RAII_VAR(struct stasis_message_router *, device_msg_router, NULL, stasis_message_router_unsubscribe);
408         RAII_VAR(struct ast_eid *, foreign_eid, NULL, ast_free);
409         RAII_VAR(int, cleanup_cache, 0, cache_cleanup);
410         int res;
411         struct ast_device_state_message *device_state;
412         struct stasis_message *msg;
413
414         switch (cmd) {
415         case TEST_INIT:
416                 info->name = "device_state_aggregation_test";
417                 info->category = "/main/devicestate/";
418                 info->summary = "Tests message routing and aggregation through the Stasis device state system.";
419                 info->description =
420                         "Verifies that the device state system passes "
421                         "messages appropriately, that the aggregator is "
422                         "working properly, that the aggregate results match "
423                         "the expected combined devstate, and that the cached "
424                         "aggregate devstate is correct.";
425                 return AST_TEST_NOT_RUN;
426         case TEST_EXECUTE:
427                 break;
428         }
429
430         foreign_eid = ast_malloc(sizeof(*foreign_eid));
431         ast_test_validate(test, NULL != foreign_eid);
432         memset(foreign_eid, 0xFF, sizeof(*foreign_eid));
433
434         consumer = consumer_create();
435         ast_test_validate(test, NULL != consumer);
436
437         device_msg_router = stasis_message_router_create(ast_device_state_topic_cached());
438         ast_test_validate(test, NULL != device_msg_router);
439
440         ao2_ref(consumer, +1);
441         res = stasis_message_router_add(device_msg_router, stasis_cache_update_type(), consumer_exec, consumer);
442         ast_test_validate(test, !res);
443
444         res = stasis_message_router_add(device_msg_router, stasis_subscription_change_type(), consumer_finalize, consumer);
445         ast_test_validate(test, !res);
446
447         /* push local state */
448         ast_publish_device_state(UNIT_TEST_DEVICE_IDENTIFIER, AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE);
449
450         consumer_wait_for(consumer);
451         ast_test_validate(test, AST_DEVICE_NOT_INUSE == consumer->state);
452         ast_test_validate(test, AST_DEVICE_NOT_INUSE == consumer->aggregate_state);
453
454         msg = stasis_cache_get(ast_device_state_cache(), ast_device_state_message_type(), UNIT_TEST_DEVICE_IDENTIFIER);
455         device_state = stasis_message_data(msg);
456         ast_test_validate(test, AST_DEVICE_NOT_INUSE == device_state->state);
457         ao2_cleanup(msg);
458         msg = NULL;
459
460         /* push remote state */
461         /* this will not produce a new aggregate state message since the aggregate state does not change */
462         consumer->sig_on_non_aggregate_state = 1;
463         ast_publish_device_state_full(UNIT_TEST_DEVICE_IDENTIFIER, AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, foreign_eid);
464
465         consumer_wait_for(consumer);
466         ast_test_validate(test, AST_DEVICE_NOT_INUSE == consumer->state);
467         ast_test_validate(test, AST_DEVICE_NOT_INUSE == consumer->aggregate_state);
468
469         msg = stasis_cache_get(ast_device_state_cache(), ast_device_state_message_type(), UNIT_TEST_DEVICE_IDENTIFIER);
470         device_state = stasis_message_data(msg);
471         ast_test_validate(test, AST_DEVICE_NOT_INUSE == device_state->state);
472         ao2_cleanup(msg);
473         msg = NULL;
474
475         /* push remote state different from local state */
476         ast_publish_device_state_full(UNIT_TEST_DEVICE_IDENTIFIER, AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, foreign_eid);
477
478         consumer_wait_for(consumer);
479         ast_test_validate(test, AST_DEVICE_INUSE == consumer->state);
480         ast_test_validate(test, AST_DEVICE_INUSE == consumer->aggregate_state);
481
482         msg = stasis_cache_get(ast_device_state_cache(), ast_device_state_message_type(), UNIT_TEST_DEVICE_IDENTIFIER);
483         device_state = stasis_message_data(msg);
484         ast_test_validate(test, AST_DEVICE_INUSE == device_state->state);
485         ao2_cleanup(msg);
486         msg = NULL;
487
488         /* push local state that will cause aggregated state different from local non-aggregate state */
489         ast_publish_device_state(UNIT_TEST_DEVICE_IDENTIFIER, AST_DEVICE_RINGING, AST_DEVSTATE_CACHABLE);
490
491         consumer_wait_for(consumer);
492         ast_test_validate(test, AST_DEVICE_RINGING == consumer->state);
493         ast_test_validate(test, AST_DEVICE_RINGINUSE == consumer->aggregate_state);
494
495         msg = stasis_cache_get(ast_device_state_cache(), ast_device_state_message_type(), UNIT_TEST_DEVICE_IDENTIFIER);
496         device_state = stasis_message_data(msg);
497         ast_test_validate(test, AST_DEVICE_RINGINUSE == device_state->state);
498         ao2_cleanup(msg);
499         msg = NULL;
500
501         return AST_TEST_PASS;
502 }
503
504 static int unload_module(void)
505 {
506         AST_TEST_UNREGISTER(device2extenstate_test);
507         AST_TEST_UNREGISTER(device_state_aggregation_test);
508         return 0;
509 }
510
511 static int load_module(void)
512 {
513         AST_TEST_REGISTER(device_state_aggregation_test);
514         AST_TEST_REGISTER(device2extenstate_test);
515         return AST_MODULE_LOAD_SUCCESS;
516 }
517
518 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Device State Test");