40 #include "asterisk/stasis_message_router.h"
43 #define UNIT_TEST_DEVICE_IDENTIFIER "unit_test_device_identifier"
45 #define DEVICE_STATE_CHANNEL_TYPE "TestDeviceState"
47 #define DEVSTATE_PROVIDER "TestDevState"
49 #define DEVSTATE_PROVIDER_LC "testdevstate"
51 #define DEVSTATE_PROVIDER_LEN 12
57 static int combined_results[] = {
141 static int exten_results[] = {
265 state = payload->
state;
268 if (ast_strlen_zero(device)) {
277 if (strncasecmp(device, DEVSTATE_PROVIDER, DEVSTATE_PROVIDER_LEN)) {
292 static int wait_for_device_state_updates(
struct ast_test *
test,
int expected_updates)
296 struct timespec wait_time = { .tv_sec = wait_now.tv_sec + 1, .tv_nsec = wait_now.tv_usec * 1000 };
301 if (error == ETIMEDOUT) {
302 ast_test_status_update(test,
"Test timed out while waiting for %d expected updates\n", expected_updates);
314 int res = AST_TEST_PASS;
322 info->name =
"device2extenstate_test";
323 info->category =
"/main/devicestate/";
324 info->summary =
"Tests combined devstate mapping and device to extension state mapping.";
326 "Verifies device state aggregate results match the expected combined "
327 "devstate. Then verifies the combined devstate maps to the expected "
329 return AST_TEST_NOT_RUN;
335 ast_test_status_update(test,
"Result array is %d long when it should be %d. "
336 "Something has changed, this test must be updated.\n",
337 (
int) ARRAY_LEN(exten_results), (AST_DEVICE_TOTAL * AST_DEVICE_TOTAL));
338 return AST_TEST_FAIL;
341 if (ARRAY_LEN(combined_results) != ARRAY_LEN(exten_results)) {
342 ast_test_status_update(test,
"combined_results and exten_results arrays do not match in length.\n");
343 return AST_TEST_FAIL;
352 if (combined_results[k] != combined) {
353 ast_test_status_update(test,
"Expected combined dev state %s "
354 "does not match %s at combined_result[%d].\n",
362 if (exten_results[k] != exten) {
363 ast_test_status_update(test,
"Expected exten state %s "
364 "does not match %s at exten_result[%d]\n",
379 int sig_on_non_aggregate_state;
385 static void consumer_dtor(
void *obj)
389 ast_cond_destroy(&consumer->out);
394 consumer->already_out = 0;
395 consumer->event_count = 0;
400 static struct consumer *consumer_create(
void)
404 consumer = ao2_alloc(
sizeof(*consumer), consumer_dtor);
409 ast_cond_init(&consumer->out, NULL);
410 consumer_reset(consumer);
417 struct consumer *consumer = data;
427 if (strcmp(device_state->
device, UNIT_TEST_DEVICE_IDENTIFIER)) {
435 ++consumer->event_count;
436 if (device_state->
eid) {
437 consumer->state = device_state->
state;
438 if (consumer->sig_on_non_aggregate_state) {
439 consumer->sig_on_non_aggregate_state = 0;
440 consumer->already_out = 1;
441 ast_cond_signal(&consumer->out);
444 consumer->aggregate_state = device_state->
state;
445 consumer->already_out = 1;
446 ast_cond_signal(&consumer->out);
453 struct consumer *consumer = data;
456 ao2_cleanup(consumer);
460 static void consumer_wait_for(
struct consumer *consumer)
464 struct timespec end = {
465 .tv_sec = start.tv_sec + 10,
466 .tv_nsec = start.tv_usec * 1000
471 while (!consumer->already_out) {
473 if (!res || res == ETIMEDOUT) {
479 static int remove_device_states_cb(
void *obj,
void *arg,
int flags)
484 if (strcmp(UNIT_TEST_DEVICE_IDENTIFIER, device_state->
device)) {
498 static void cache_cleanup(
int unused)
507 ao2_callback(cache_dump, 0, remove_device_states_cb, NULL);
508 ao2_cleanup(cache_dump);
513 RAII_VAR(
struct consumer *, consumer, NULL, ao2_cleanup);
516 RAII_VAR(
int, cleanup_cache, 0, cache_cleanup);
523 info->name =
"device_state_aggregation_test";
524 info->category =
"/main/devicestate/";
525 info->summary =
"Tests message routing and aggregation through the Stasis device state system.";
527 "Verifies that the device state system passes "
528 "messages appropriately, that the aggregator is "
529 "working properly, that the aggregate results match "
530 "the expected combined devstate, and that the cached "
531 "aggregate devstate is correct.";
532 return AST_TEST_NOT_RUN;
537 foreign_eid =
ast_malloc(
sizeof(*foreign_eid));
538 ast_test_validate(test, NULL != foreign_eid);
539 memset(foreign_eid, 0xFF,
sizeof(*foreign_eid));
541 consumer = consumer_create();
542 ast_test_validate(test, NULL != consumer);
545 ast_test_validate(test, NULL != device_msg_router);
549 ast_test_validate(test, !res);
552 ast_test_validate(test, !res);
563 consumer_wait_for(consumer);
566 ast_test_validate(test, 2 == consumer->event_count);
567 consumer_reset(consumer);
571 consumer->sig_on_non_aggregate_state = 1;
581 consumer_wait_for(consumer);
583 ast_test_validate(test, AST_DEVICE_TOTAL == consumer->aggregate_state);
584 ast_test_validate(test, 1 == consumer->event_count);
585 consumer_reset(consumer);
597 consumer_wait_for(consumer);
600 ast_test_validate(test, 2 == consumer->event_count);
601 consumer_reset(consumer);
613 consumer_wait_for(consumer);
616 ast_test_validate(test, 2 == consumer->event_count);
617 consumer_reset(consumer);
619 return AST_TEST_PASS;
626 info->name = __func__;
627 info->category =
"/main/devicestate/";
628 info->summary =
"Test adding a device state provider";
630 "Test that a custom device state provider can be added, and that\n"
631 "it cannot be added if already added.";
632 return AST_TEST_NOT_RUN;
641 return AST_TEST_PASS;
648 info->name = __func__;
649 info->category =
"/main/devicestate/";
650 info->summary =
"Test removing a device state provider";
652 "Test that a custom device state provider can be removed, and that\n"
653 "it cannot be removed if already removed.";
654 return AST_TEST_NOT_RUN;
663 return AST_TEST_PASS;
683 info->name = __func__;
684 info->category =
"/main/devicestate/";
685 info->summary =
"Test updates coming from a device state provider";
687 "This unit test checks that a custom device state provider can\n"
688 "have updates published for it. This includes both cacheable and\n"
689 "non-cacheable events. In the case of non-cacheable events, the\n"
690 "device state provider's callback function is queried for the\n"
691 "device state when AST_DEVICE_UNKNOWN is published.";
692 return AST_TEST_NOT_RUN;
713 ast_test_validate(test, wait_for_device_state_updates(test, 8) == 0);
716 ast_test_status_update(test,
"Testing update %d: actual is %d; expected is %d\n",
719 expected_results[i]);
733 ast_test_validate(test, wait_for_device_state_updates(test, 1) == 0);
752 ast_test_validate(test, wait_for_device_state_updates(test, 8) == 0);
754 ast_test_status_update(test,
"Testing update %d: actual is %d; expected is %d\n",
757 expected_results[i]);
785 return AST_TEST_PASS;
792 info->name = __func__;
793 info->category =
"/main/devicestate/";
794 info->summary =
"Test ast_device_state conversions";
796 "Test various transformations of ast_device_state values.";
797 return AST_TEST_NOT_RUN;
835 return AST_TEST_PASS;
846 struct timespec wait_time = { .tv_sec = wait_now.tv_sec + 1, .tv_nsec = wait_now.tv_usec * 1000 };
849 while (!chan_callback_called) {
851 if (error == ETIMEDOUT) {
852 ast_test_status_update(test,
"Test timed out while waiting channel callback\n");
861 static void safe_hangup(
void *
object)
877 info->name = __func__;
878 info->category =
"/main/devicestate/";
879 info->summary =
"Test deriving device state from a channel's state";
881 "Test querying a channel's state to derive a device state.";
882 return AST_TEST_NOT_RUN;
887 chan_callback_called = 0;
890 NULL, NULL, 0, DEVICE_STATE_CHANNEL_TYPE
"/foo-%08x",
892 ast_test_validate(test, chan != NULL);
902 chan_callback_called = 0;
904 return AST_TEST_PASS;
907 static int chan_test_devicestate_cb(
const char *device_number)
910 chan_callback_called = 1;
920 .type = DEVICE_STATE_CHANNEL_TYPE,
921 .description =
"Device State Unit Test Channel Driver",
922 .devicestate = chan_test_devicestate_cb,
925 static int unload_module(
void)
930 AST_TEST_UNREGISTER(device2extenstate_test);
931 AST_TEST_UNREGISTER(device_state_aggregation_test);
933 AST_TEST_UNREGISTER(devstate_prov_add);
934 AST_TEST_UNREGISTER(devstate_prov_del);
936 AST_TEST_UNREGISTER(devstate_changed);
937 AST_TEST_UNREGISTER(devstate_conversions);
939 AST_TEST_UNREGISTER(devstate_channels);
944 static int load_module(
void)
955 AST_TEST_REGISTER(device_state_aggregation_test);
956 AST_TEST_REGISTER(device2extenstate_test);
958 AST_TEST_REGISTER(devstate_prov_add);
959 AST_TEST_REGISTER(devstate_prov_del);
961 AST_TEST_REGISTER(devstate_changed);
962 AST_TEST_REGISTER(devstate_conversions);
964 AST_TEST_REGISTER(devstate_channels);
struct stasis_topic * ast_device_state_topic(const char *device)
Get the Stasis topic for device state messages for a specific device.
struct ao2_container * stasis_cache_dump_all(struct stasis_cache *cache, struct stasis_message_type *type)
Dump all entity items from the cache to a subscription.
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
const char * ast_devstate2str(enum ast_device_state devstate) attribute_pure
Convert device state to text string for output.
Main Channel structure associated with a channel.
ast_device_state
Device States.
ast_extension_states
Extension states.
Asterisk main include file. File version handling, generic pbx functions.
static enum ast_device_state current_device_state
The current device state for our device state provider.
int stasis_message_router_add(struct stasis_message_router *router, struct stasis_message_type *message_type, stasis_subscription_cb callback, void *data)
Add a route to a message router.
static struct @504 result_states
The resulting device state updates caused by some function call.
static ast_mutex_t update_lock
Mutex for update_cond.
void ast_channel_unregister(const struct ast_channel_tech *tech)
Unregister a channel technology.
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
enum ast_device_state state
static unsigned int chan_idx
Used to assign an increasing integer to channel name.
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
enum ast_device_state ast_parse_device_state(const char *device)
Search the Channels by Name.
int ast_devstate_prov_del(const char *label)
Remove device state provider.
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
#define AST_VECTOR_REMOVE_UNORDERED(vec, idx)
Remove an element from an unordered vector by index.
struct stasis_message * stasis_cache_clear_create(struct stasis_message *message)
A message which instructs the caching topic to remove an entry from its cache.
int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
Add device state provider.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
struct stasis_message * stasis_cache_get_by_eid(struct stasis_cache *cache, struct stasis_message_type *type, const char *id, const struct ast_eid *eid)
Retrieve an item from the cache for a specific entity.
int ast_channel_register(const struct ast_channel_tech *tech)
Register a channel technology (a new channel driver) Called by a channel module to register the kind ...
An Entity ID is essentially a MAC address, brief and unique.
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
enum ast_device_state ast_devstate_aggregate_result(struct ast_devstate_aggregate *agg)
Get the aggregate device state result.
static ast_mutex_t channel_cb_lock
Mutext for channel_cb_cond.
static int chan_callback_called
Whether or not the channel device state callback was called.
void * ao2_object_get_lockaddr(void *obj)
Return the mutex lock address of an object.
static void device_state_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
Stasis subscription callback for device state updates.
struct stasis_message_type * ast_device_state_message_type(void)
Get the Stasis message type for device state messages.
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
struct stasis_topic * ast_device_state_topic_cached(void)
Get the Stasis caching topic for device state messages.
enum ast_device_state ast_devstate_val(const char *val)
Convert device state from text to integer value.
const struct ast_eid * eid
The EID of the server where this message originated.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
const char * ast_extension_state2str(int extension_state)
Return string representation of the state of an extension.
struct stasis_message_type * stasis_cache_update_type(void)
Message type for cache update messages.
#define ast_malloc(len)
A wrapper for malloc()
enum ast_device_state ast_state_chan2dev(enum ast_channel_state chanstate)
Convert channel state to devicestate.
#define AST_VECTOR(name, type)
Define a vector structure.
Structure to describe a channel "technology", ie a channel driver See for examples: ...
Core PBX routines and definitions.
static int cache_update(struct stasis_message *msg, const void *data)
Message matcher looking for cache update messages.
void ast_devstate_aggregate_add(struct ast_devstate_aggregate *agg, enum ast_device_state state)
Add a device state to the aggregate device state.
const char * ast_devstate_str(enum ast_device_state devstate) attribute_pure
Convert device state to text string that is easier to parse.
struct stasis_message * new_snapshot
New value.
int ast_publish_device_state_full(const char *device, enum ast_device_state state, enum ast_devstate_cache cachable, struct ast_eid *eid)
Publish a device state update with EID.
struct stasis_cache * ast_device_state_cache(void)
Backend cache for ast_device_state_topic_cached()
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
struct stasis_topic * ast_device_state_topic_all(void)
Get the Stasis topic for device state messages.
int stasis_subscription_final_message(struct stasis_subscription *sub, struct stasis_message *msg)
Determine whether a message is the final message to be received on a subscription.
enum ast_extension_states ast_devstate_to_extenstate(enum ast_device_state devstate)
Map devstate to an extension state.
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
void ast_devstate_aggregate_init(struct ast_devstate_aggregate *agg)
Initialize aggregate device state.
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
Module has failed to load, may be in an inconsistent state.
Vector container support.
static ast_cond_t channel_cb_cond
Condition wait variable for channel tech device state cb.
void stasis_message_router_unsubscribe(struct stasis_message_router *router)
Unsubscribe the router from the upstream topic.
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
static int wait_for_channel_callback(struct ast_test *test)
Wait until the test channel driver's devicestate callback is called.
static void clear_result_states(void)
Clear out all recorded device states in result_states.
int ast_setstate(struct ast_channel *chan, enum ast_channel_state)
Change the state of a channel.
#define AST_TEST_DEFINE(hdr)
You shouldn't care about the contents of this struct.
The structure that contains device state.
struct stasis_message_type * stasis_subscription_change_type(void)
Gets the message type for subscription change notices.
int ast_devstate_changed_literal(enum ast_device_state state, enum ast_devstate_cache cachable, const char *device)
Tells Asterisk the State for Device is changed.
#define ASTERISK_GPL_KEY
The text the key() function should return.
#define ast_channel_alloc(needqueue, state, cid_num, cid_name, acctcode, exten, context, assignedids, requestor, amaflag,...)
Create a channel structure.
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
static ast_cond_t update_cond
Condition wait variable for device state updates.
#define ast_publish_device_state(device, state, cachable)
Publish a device state update.