[asterisk-commits] kmoore: branch kmoore/cel_cleanup r393022 - in /team/kmoore/cel_cleanup: main...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Wed Jun 26 16:39:34 CDT 2013
Author: kmoore
Date: Wed Jun 26 16:39:32 2013
New Revision: 393022
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=393022
Log:
Add CEL unit tests and cleanup CEL inner workings
Added:
team/kmoore/cel_cleanup/tests/test_cel.c (with props)
Modified:
team/kmoore/cel_cleanup/main/cel.c
Modified: team/kmoore/cel_cleanup/main/cel.c
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/cel_cleanup/main/cel.c?view=diff&rev=393022&r1=393021&r2=393022
==============================================================================
--- team/kmoore/cel_cleanup/main/cel.c (original)
+++ team/kmoore/cel_cleanup/main/cel.c Wed Jun 26 16:39:32 2013
@@ -637,7 +637,6 @@
const char *extra, const char *peer_name)
{
struct timeval eventtime = ast_tvnow();
- ast_log(LOG_ERROR, "Creating %d event with extra: %s\n", event_type, S_OR(extra, ""));
return ast_event_new(AST_EVENT_CEL,
AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_PLTYPE_UINT, event_type,
AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_sec,
@@ -1211,12 +1210,9 @@
cel_channel_monitors[i](old_snapshot, new_snapshot);
}
} else if (ast_bridge_snapshot_type() == update->type) {
- RAII_VAR(struct bridge_assoc *, assoc, NULL, ao2_cleanup);
struct ast_bridge_snapshot *old_snapshot;
struct ast_bridge_snapshot *new_snapshot;
- update = stasis_message_data(message);
-
old_snapshot = stasis_message_data(update->old_snapshot);
new_snapshot = stasis_message_data(update->new_snapshot);
@@ -1226,62 +1222,6 @@
if (!new_snapshot) {
clear_bridge_primary(old_snapshot->uniqueid);
- return;
- }
-
- if (old_snapshot->capabilities == new_snapshot->capabilities) {
- return;
- }
-
- /* handle 1:1/native -> multimix */
- if ((old_snapshot->capabilities & (AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_NATIVE))
- && (new_snapshot->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX)) {
- assoc = find_bridge_primary_by_bridge_id(new_snapshot->uniqueid);
- if (!assoc) {
- ast_log(LOG_ERROR, "No association found for bridge %s\n", new_snapshot->uniqueid);
- return;
- }
-
- /* this bridge will no longer be treated like a bridge, so mark the bridge_assoc as such */
- assoc->track_as_conf = 1;
- report_event_snapshot(assoc->primary_snapshot, AST_CEL_BRIDGE_TO_CONF, NULL, NULL, assoc->secondary_name);
- return;
- }
-
- /* handle multimix -> 1:1/native */
- if ((old_snapshot->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX)
- && (new_snapshot->capabilities & (AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_NATIVE))) {
- struct ao2_iterator i;
- RAII_VAR(char *, channel_id, NULL, ao2_cleanup);
- RAII_VAR(struct ast_channel_snapshot *, chan_snapshot, NULL, ao2_cleanup);
-
- assoc = find_bridge_primary_by_bridge_id(new_snapshot->uniqueid);
- if (assoc) {
- assoc->track_as_conf = 1;
- return;
- }
-
- /* get the first item in the container */
- i = ao2_iterator_init(new_snapshot->channels, 0);
- while ((channel_id = ao2_iterator_next(&i))) {
- break;
- }
- ao2_iterator_destroy(&i);
-
- /* create a bridge_assoc for this bridge and mark it as being tracked appropriately */
- chan_snapshot = ast_channel_snapshot_get_latest(channel_id);
- if (!chan_snapshot) {
- return;
- }
-
- ast_assert(chan_snapshot != NULL);
- assoc = bridge_assoc_alloc(chan_snapshot, new_snapshot->uniqueid, chan_snapshot->name);
- if (!assoc) {
- return;
- }
- assoc->track_as_conf = 1;
-
- ao2_link(bridge_primaries, assoc);
return;
}
}
@@ -1295,9 +1235,9 @@
struct ast_bridge_blob *blob = stasis_message_data(message);
struct ast_bridge_snapshot *snapshot = blob->bridge;
struct ast_channel_snapshot *chan_snapshot = blob->channel;
+ RAII_VAR(struct bridge_assoc *, assoc, find_bridge_primary_by_bridge_id(snapshot->uniqueid), ao2_cleanup);
if (snapshot->capabilities & (AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_NATIVE)) {
- RAII_VAR(struct bridge_assoc *, assoc, find_bridge_primary_by_bridge_id(snapshot->uniqueid), ao2_cleanup);
if (assoc && assoc->track_as_conf) {
report_event_snapshot(chan_snapshot, AST_CEL_CONF_ENTER, NULL, NULL, NULL);
return;
@@ -1326,8 +1266,25 @@
add_bridge_primary(latest_primary, snapshot->uniqueid, chan_snapshot->name);
report_event_snapshot(latest_primary, AST_CEL_BRIDGE_START, NULL, NULL, chan_snapshot->name);
+ } else if (ao2_container_count(snapshot->channels) > 2) {
+ if (!assoc) {
+ ast_log(LOG_ERROR, "No association found for bridge %s\n", snapshot->uniqueid);
+ return;
+ }
+
+ /* this bridge will no longer be treated like a bridge, so mark the bridge_assoc as such */
+ if (!assoc->track_as_conf) {
+ assoc->track_as_conf = 1;
+ report_event_snapshot(assoc->primary_snapshot, AST_CEL_BRIDGE_TO_CONF, NULL,
+ chan_snapshot->name, assoc->secondary_name);
+ ast_string_field_set(assoc, secondary_name, "");
+ }
}
} else if (snapshot->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) {
+ if (!assoc) {
+ add_bridge_primary(chan_snapshot, snapshot->uniqueid, "");
+ return;
+ }
report_event_snapshot(chan_snapshot, AST_CEL_CONF_ENTER, NULL, NULL, NULL);
}
}
Added: team/kmoore/cel_cleanup/tests/test_cel.c
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/cel_cleanup/tests/test_cel.c?view=auto&rev=393022
==============================================================================
--- team/kmoore/cel_cleanup/tests/test_cel.c (added)
+++ team/kmoore/cel_cleanup/tests/test_cel.c Wed Jun 26 16:39:32 2013
@@ -1,0 +1,1445 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Kinsey Moore <kmoore at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief CEL unit tests
+ *
+ * \author Kinsey Moore <kmoore at digium.com>
+ *
+ */
+
+/*** MODULEINFO
+ <depend>TEST_FRAMEWORK</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <math.h>
+#include "asterisk/module.h"
+#include "asterisk/test.h"
+#include "asterisk/cel.h"
+#include "asterisk/channel.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/chanvars.h"
+#include "asterisk/utils.h"
+#include "asterisk/causes.h"
+#include "asterisk/time.h"
+#include "asterisk/bridging.h"
+#include "asterisk/bridging_basic.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/stasis_bridging.h"
+
+#define TEST_CATEGORY "/main/cel/"
+
+#define CHANNEL_TECH_NAME "CELTestChannel"
+
+/*! \brief A placeholder for Asterisk's 'real' CEL configuration */
+static struct ast_cel_config *saved_config;
+
+/*! \brief The CEL config used for CEL unit tests */
+static struct ast_cel_config *cel_test_config;
+
+/*! \brief A channel technology used for the unit tests */
+static struct ast_channel_tech test_cel_chan_tech = {
+ .type = CHANNEL_TECH_NAME,
+ .description = "Mock channel technology for CEL tests",
+};
+
+/*! \brief A 1 second sleep */
+static struct timespec to_sleep = {1, 0};
+
+static void do_sleep(void)
+{
+ while ((nanosleep(&to_sleep, &to_sleep) == -1) && (errno == EINTR));
+}
+
+#define APPEND_EVENT(chan, ev_type, userevent, extra, peer) do { \
+ if (append_expected_event(chan, ev_type, userevent, extra, peer)) { \
+ return AST_TEST_FAIL; \
+ } \
+ } while (0)
+
+/*! \brief Alice's Caller ID */
+#define ALICE_CALLERID { .id.name.str = "Alice", .id.name.valid = 1, .id.number.str = "100", .id.number.valid = 1, }
+
+/*! \brief Bob's Caller ID */
+#define BOB_CALLERID { .id.name.str = "Bob", .id.name.valid = 1, .id.number.str = "200", .id.number.valid = 1, }
+
+/*! \brief Charlie's Caller ID */
+#define CHARLIE_CALLERID { .id.name.str = "Charlie", .id.name.valid = 1, .id.number.str = "300", .id.number.valid = 1, }
+
+/*! \brief David's Caller ID */
+#define DAVID_CALLERID { .id.name.str = "David", .id.name.valid = 1, .id.number.str = "400", .id.number.valid = 1, }
+
+/*! \brief Create a \ref test_cel_chan_tech for Alice. */
+#define CREATE_ALICE_CHANNEL(channel_var, caller_id) do { \
+ (channel_var) = ast_channel_alloc(0, AST_STATE_DOWN, (caller_id)->id.number.str, (caller_id)->id.name.str, "100", "100", "default", NULL, 0, CHANNEL_TECH_NAME "/Alice"); \
+ /*ast_channel_set_caller((channel_var), (caller_id), NULL);*/ \
+ APPEND_EVENT(channel_var, AST_CEL_CHANNEL_START, NULL, NULL, NULL); \
+ } while (0)
+
+/*! \brief Create a \ref test_cel_chan_tech for Bob. */
+#define CREATE_BOB_CHANNEL(channel_var, caller_id) do { \
+ (channel_var) = ast_channel_alloc(0, AST_STATE_DOWN, (caller_id)->id.number.str, (caller_id)->id.name.str, "200", "200", "default", NULL, 0, CHANNEL_TECH_NAME "/Bob"); \
+ /*ast_channel_set_caller((channel_var), (caller_id), NULL);*/ \
+ APPEND_EVENT(channel_var, AST_CEL_CHANNEL_START, NULL, NULL, NULL); \
+ } while (0)
+
+/*! \brief Create a \ref test_cel_chan_tech for Charlie. */
+#define CREATE_CHARLIE_CHANNEL(channel_var, caller_id) do { \
+ (channel_var) = ast_channel_alloc(0, AST_STATE_DOWN, (caller_id)->id.number.str, (caller_id)->id.name.str, "300", "300", "default", NULL, 0, CHANNEL_TECH_NAME "/Charlie"); \
+ /*ast_channel_set_caller((channel_var), (caller_id), NULL);*/ \
+ APPEND_EVENT(channel_var, AST_CEL_CHANNEL_START, NULL, NULL, NULL); \
+ } while (0)
+
+/*! \brief Create a \ref test_cel_chan_tech for Charlie. */
+#define CREATE_DAVID_CHANNEL(channel_var, caller_id) do { \
+ (channel_var) = ast_channel_alloc(0, AST_STATE_DOWN, (caller_id)->id.number.str, (caller_id)->id.name.str, "400", "400", "default", NULL, 0, CHANNEL_TECH_NAME "/David"); \
+ /*ast_channel_set_caller((channel_var), (caller_id), NULL);*/ \
+ APPEND_EVENT(channel_var, AST_CEL_CHANNEL_START, NULL, NULL, NULL); \
+ } while (0)
+
+/*! \brief Emulate a channel entering into an application */
+#define EMULATE_APP_DATA(channel, priority, application, data) do { \
+ if ((priority) > 0) { \
+ ast_channel_priority_set((channel), (priority)); \
+ } \
+ ast_channel_appl_set((channel), (application)); \
+ ast_channel_data_set((channel), (data)); \
+ ast_channel_publish_snapshot((channel)); \
+ } while (0)
+
+#define ANSWER_CHANNEL(chan) do { \
+ EMULATE_APP_DATA(chan, 1, "Answer", ""); \
+ ANSWER_NO_APP(chan); \
+ } while (0)
+
+#define ANSWER_NO_APP(chan) do { \
+ ast_setstate(chan, AST_STATE_UP); \
+ APPEND_EVENT(chan, AST_CEL_ANSWER, NULL, NULL, NULL); \
+ } while (0)
+
+/*! \brief Hang up a test channel safely */
+#define HANGUP_CHANNEL(channel, cause, hangup_extra) do { \
+ ast_channel_hangupcause_set((channel), (cause)); \
+ ao2_ref(channel, +1); \
+ if (!ast_hangup((channel))) { \
+ APPEND_EVENT(channel, AST_CEL_HANGUP, NULL, hangup_extra, NULL); \
+ APPEND_EVENT(channel, AST_CEL_CHANNEL_END, NULL, NULL, NULL); \
+ ao2_cleanup(stasis_cache_get_extended(ast_channel_topic_all_cached(), \
+ ast_channel_snapshot_type(), ast_channel_uniqueid(channel), 1)); \
+ ao2_cleanup(channel); \
+ channel = NULL; \
+ } else { \
+ APPEND_EVENT(channel, AST_CEL_HANGUP, NULL, hangup_extra, NULL); \
+ APPEND_EVENT(channel, AST_CEL_CHANNEL_END, NULL, NULL, NULL); \
+ ao2_cleanup(stasis_cache_get_extended(ast_channel_topic_all_cached(), \
+ ast_channel_snapshot_type(), ast_channel_uniqueid(channel), 1)); \
+ ao2_cleanup(channel); \
+ } \
+ } while (0)
+
+static int append_expected_event(
+ struct ast_channel *chan,
+ enum ast_cel_event_type type,
+ const char *userdefevname,
+ const char *extra, const char *peer);
+
+static void safe_channel_release(struct ast_channel *chan)
+{
+ if (!chan) {
+ return;
+ }
+ ast_channel_release(chan);
+}
+
+AST_TEST_DEFINE(test_cel_channel_creation)
+{
+ RAII_VAR(struct ast_channel *, chan, NULL, safe_channel_release);
+ struct ast_party_caller caller = ALICE_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test the CEL records created when a channel is created";
+ info->description =
+ "Test the CEL records created when a channel is created";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ CREATE_ALICE_CHANNEL(chan, (&caller));
+
+ HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "16,,");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_cel_unanswered_inbound_call)
+{
+ RAII_VAR(struct ast_channel *, chan, NULL, safe_channel_release);
+ struct ast_party_caller caller = ALICE_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test inbound unanswered calls";
+ info->description =
+ "Test CEL records for a call that is\n"
+ "inbound to Asterisk, executes some dialplan, but\n"
+ "is never answered.\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ CREATE_ALICE_CHANNEL(chan, &caller);
+
+ EMULATE_APP_DATA(chan, 1, "Wait", "1");
+
+ HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "16,,");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_cel_unanswered_outbound_call)
+{
+ RAII_VAR(struct ast_channel *, chan, NULL, safe_channel_release);
+ struct ast_party_caller caller = {
+ .id.name.str = "",
+ .id.name.valid = 1,
+ .id.number.str = "",
+ .id.number.valid = 1, };
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test outbound unanswered calls";
+ info->description =
+ "Test CEL records for a call that is\n"
+ "outbound to Asterisk but is never answered.\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ CREATE_ALICE_CHANNEL(chan, &caller);
+
+ ast_channel_exten_set(chan, "s");
+ ast_channel_context_set(chan, "default");
+ ast_set_flag(ast_channel_flags(chan), AST_FLAG_ORIGINATED);
+ EMULATE_APP_DATA(chan, 0, "AppDial", "(Outgoing Line)");
+ HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "16,,");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_cel_single_party)
+{
+ RAII_VAR(struct ast_channel *, chan, NULL, safe_channel_release);
+ struct ast_party_caller caller = ALICE_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test CEL for a single party";
+ info->description =
+ "Test CEL records for a call that is\n"
+ "answered, but only involves a single channel\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+ CREATE_ALICE_CHANNEL(chan, &caller);
+
+ ANSWER_CHANNEL(chan);
+ EMULATE_APP_DATA(chan, 2, "VoiceMailMain", "1");
+
+ HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "16,,");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_cel_single_bridge)
+{
+ RAII_VAR(struct ast_channel *, chan, NULL, safe_channel_release);
+ RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
+
+ struct ast_party_caller caller = ALICE_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test CEL for a single party entering/leaving a bridge";
+ info->description =
+ "Test CEL records for a call that is\n"
+ "answered, enters a bridge, and leaves it.\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+ bridge = ast_bridge_basic_new();
+ ast_test_validate(test, bridge != NULL);
+
+ CREATE_ALICE_CHANNEL(chan, &caller);
+
+ ANSWER_CHANNEL(chan);
+ EMULATE_APP_DATA(chan, 2, "Bridge", "");
+
+ do_sleep();
+ ast_bridge_impart(bridge, chan, NULL, NULL, 0);
+
+ do_sleep();
+
+ ast_bridge_depart(chan);
+
+ HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "16,,");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_cel_single_bridge_continue)
+{
+ RAII_VAR(struct ast_channel *, chan, NULL, safe_channel_release);
+ RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
+ struct ast_party_caller caller = ALICE_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test CEL for a single party entering/leaving a bridge";
+ info->description =
+ "Test CEL records for a call that is\n"
+ "answered, enters a bridge, and leaves it.\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+ bridge = ast_bridge_basic_new();
+ ast_test_validate(test, bridge != NULL);
+
+ CREATE_ALICE_CHANNEL(chan, &caller);
+
+ ANSWER_CHANNEL(chan);
+ EMULATE_APP_DATA(chan, 2, "Bridge", "");
+
+ do_sleep();
+ ast_bridge_impart(bridge, chan, NULL, NULL, 0);
+
+ do_sleep();
+
+ ast_bridge_depart(chan);
+
+ EMULATE_APP_DATA(chan, 3, "Wait", "");
+
+ /* And then it hangs up */
+ HANGUP_CHANNEL(chan, AST_CAUSE_NORMAL, "16,,");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_cel_single_twoparty_bridge_a)
+{
+ RAII_VAR(struct ast_channel *, chan_alice, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_bob, NULL, safe_channel_release);
+ RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
+ struct ast_party_caller caller_alice = ALICE_CALLERID;
+ struct ast_party_caller caller_bob = BOB_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test CEL for a single party entering/leaving a bridge";
+ info->description =
+ "Test CEL records for a call that is\n"
+ "answered, enters a bridge, and leaves it. In this scenario, the\n"
+ "Party A should answer the bridge first.\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+ bridge = ast_bridge_basic_new();
+ ast_test_validate(test, bridge != NULL);
+
+ CREATE_ALICE_CHANNEL(chan_alice, &caller_alice);
+
+ CREATE_BOB_CHANNEL(chan_bob, &caller_bob);
+
+ ANSWER_CHANNEL(chan_alice);
+ EMULATE_APP_DATA(chan_alice, 2, "Bridge", "");
+
+ ast_bridge_impart(bridge, chan_alice, NULL, NULL, 0);
+ do_sleep();
+
+ ANSWER_CHANNEL(chan_bob);
+ EMULATE_APP_DATA(chan_bob, 2, "Bridge", "");
+
+ ast_bridge_impart(bridge, chan_bob, NULL, NULL, 0);
+ do_sleep();
+ APPEND_EVENT(chan_alice, AST_CEL_BRIDGE_START, NULL, NULL, ast_channel_name(chan_bob));
+
+ ast_bridge_depart(chan_alice);
+ ast_bridge_depart(chan_bob);
+ APPEND_EVENT(chan_alice, AST_CEL_BRIDGE_END, NULL, NULL, ast_channel_name(chan_bob));
+
+ HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "16,,");
+ HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "16,,");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_cel_single_twoparty_bridge_b)
+{
+ RAII_VAR(struct ast_channel *, chan_alice, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_bob, NULL, safe_channel_release);
+ RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
+ struct ast_party_caller caller_alice = ALICE_CALLERID;
+ struct ast_party_caller caller_bob = BOB_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test CEL for a single party entering/leaving a bridge";
+ info->description =
+ "Test CEL records for a call that is\n"
+ "answered, enters a bridge, and leaves it. In this scenario, the\n"
+ "Party B should answer the bridge first.\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+ bridge = ast_bridge_basic_new();
+ ast_test_validate(test, bridge != NULL);
+
+ CREATE_ALICE_CHANNEL(chan_alice, &caller_alice);
+
+ CREATE_BOB_CHANNEL(chan_bob, &caller_bob);
+
+ ANSWER_CHANNEL(chan_alice);
+ EMULATE_APP_DATA(chan_alice, 2, "Bridge", "");
+
+ ANSWER_CHANNEL(chan_bob);
+ EMULATE_APP_DATA(chan_bob, 2, "Bridge", "");
+ do_sleep();
+
+ ast_bridge_impart(bridge, chan_bob, NULL, NULL, 0);
+ do_sleep();
+
+ ast_bridge_impart(bridge, chan_alice, NULL, NULL, 0);
+ do_sleep();
+ APPEND_EVENT(chan_bob, AST_CEL_BRIDGE_START, NULL, NULL, ast_channel_name(chan_alice));
+
+ ast_bridge_depart(chan_alice);
+ ast_bridge_depart(chan_bob);
+ APPEND_EVENT(chan_bob, AST_CEL_BRIDGE_END, NULL, NULL, ast_channel_name(chan_alice));
+
+ HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "16,,");
+ HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "16,,");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_cel_single_multiparty_bridge)
+{
+ RAII_VAR(struct ast_channel *, chan_alice, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_bob, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_charlie, NULL, safe_channel_release);
+ RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
+ struct ast_party_caller caller_alice = ALICE_CALLERID;
+ struct ast_party_caller caller_bob = BOB_CALLERID;
+ struct ast_party_caller caller_charlie = CHARLIE_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test CEL for a single party entering/leaving a multi-party bridge";
+ info->description =
+ "Test CEL records for a call that is\n"
+ "answered, enters a bridge, and leaves it. A total of three\n"
+ "parties perform this action.\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+ bridge = ast_bridge_basic_new();
+ ast_test_validate(test, bridge != NULL);
+
+ CREATE_ALICE_CHANNEL(chan_alice, &caller_alice);
+ CREATE_BOB_CHANNEL(chan_bob, &caller_bob);
+ CREATE_CHARLIE_CHANNEL(chan_charlie, &caller_charlie);
+
+ ANSWER_CHANNEL(chan_alice);
+ EMULATE_APP_DATA(chan_alice, 2, "Bridge", "");
+
+ do_sleep();
+
+ ast_bridge_impart(bridge, chan_alice, NULL, NULL, 0);
+
+ ANSWER_CHANNEL(chan_bob);
+ EMULATE_APP_DATA(chan_bob, 2, "Bridge", "");
+ do_sleep();
+
+ ast_bridge_impart(bridge, chan_bob, NULL, NULL, 0);
+ do_sleep();
+ APPEND_EVENT(chan_alice, AST_CEL_BRIDGE_START, NULL, NULL, ast_channel_name(chan_bob));
+
+ ANSWER_CHANNEL(chan_charlie);
+ EMULATE_APP_DATA(chan_charlie, 2, "Bridge", "");
+ ast_bridge_impart(bridge, chan_charlie, NULL, NULL, 0);
+ do_sleep();
+ APPEND_EVENT(chan_alice, AST_CEL_BRIDGE_TO_CONF, NULL, ast_channel_name(chan_charlie), ast_channel_name(chan_bob));
+
+ ast_bridge_depart(chan_alice);
+ APPEND_EVENT(chan_alice, AST_CEL_CONF_EXIT, NULL, NULL, NULL);
+ ast_bridge_depart(chan_bob);
+ APPEND_EVENT(chan_bob, AST_CEL_CONF_EXIT, NULL, NULL, NULL);
+ ast_bridge_depart(chan_charlie);
+ APPEND_EVENT(chan_charlie, AST_CEL_CONF_EXIT, NULL, NULL, NULL);
+
+ HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "16,,");
+ HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "16,,");
+ HANGUP_CHANNEL(chan_charlie, AST_CAUSE_NORMAL, "16,,");
+
+ return AST_TEST_PASS;
+}
+
+#define EMULATE_DIAL(channel, dialstring) do { \
+ EMULATE_APP_DATA(channel, 1, "Dial", dialstring); \
+ if (append_expected_event(channel, AST_CEL_APP_START, NULL, NULL, NULL)) { \
+ return AST_TEST_FAIL; \
+ } \
+ } while (0)
+
+#define START_DIALED(caller, callee) \
+ START_DIALED_FULL(caller, callee, "200", "Bob")
+
+#define START_DIALED_FULL(caller, callee, number, name) do { \
+ callee = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, number, NULL, NULL, ast_channel_linkedid(caller), 0, CHANNEL_TECH_NAME "/" name); \
+ if (append_expected_event(callee, AST_CEL_CHANNEL_START, NULL, NULL, NULL)) { \
+ return AST_TEST_FAIL; \
+ } \
+ ast_set_flag(ast_channel_flags(callee), AST_FLAG_OUTGOING); \
+ EMULATE_APP_DATA(callee, 0, "AppDial", "(Outgoing Line)"); \
+ ast_channel_publish_dial(caller, callee, name, NULL); \
+ } while (0)
+
+AST_TEST_DEFINE(test_cel_dial_unanswered)
+{
+ RAII_VAR(struct ast_channel *, chan_caller, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_callee, NULL, safe_channel_release);
+ struct ast_party_caller caller = ALICE_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test CEL for a dial that isn't answered";
+ info->description =
+ "Test CEL records for a channel that\n"
+ "performs a dial operation that isn't answered\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ CREATE_ALICE_CHANNEL(chan_caller, &caller);
+
+ EMULATE_DIAL(chan_caller, CHANNEL_TECH_NAME "/Bob");
+
+ START_DIALED(chan_caller, chan_callee);
+
+ ast_channel_state_set(chan_caller, AST_STATE_RINGING);
+ ast_channel_publish_dial(chan_caller, chan_callee, NULL, "NOANSWER");
+
+ HANGUP_CHANNEL(chan_caller, AST_CAUSE_NO_ANSWER, "19,,NOANSWER");
+ HANGUP_CHANNEL(chan_callee, AST_CAUSE_NO_ANSWER, "19,,");
+
+ return AST_TEST_PASS;
+}
+
+
+AST_TEST_DEFINE(test_cel_dial_busy)
+{
+ RAII_VAR(struct ast_channel *, chan_caller, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_callee, NULL, safe_channel_release);
+ struct ast_party_caller caller = ALICE_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test CEL for a dial that results in a busy";
+ info->description =
+ "Test CEL records for a channel that\n"
+ "performs a dial operation to an endpoint that's busy\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ CREATE_ALICE_CHANNEL(chan_caller, &caller);
+
+ EMULATE_DIAL(chan_caller, CHANNEL_TECH_NAME "/Bob");
+
+ START_DIALED(chan_caller, chan_callee);
+
+ ast_channel_state_set(chan_caller, AST_STATE_RINGING);
+ ast_channel_publish_dial(chan_caller, chan_callee, NULL, "BUSY");
+
+ HANGUP_CHANNEL(chan_caller, AST_CAUSE_BUSY, "17,,BUSY");
+ HANGUP_CHANNEL(chan_callee, AST_CAUSE_BUSY, "17,,");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_cel_dial_congestion)
+{
+ RAII_VAR(struct ast_channel *, chan_caller, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_callee, NULL, safe_channel_release);
+ struct ast_party_caller caller = ALICE_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test CEL for a dial that results in congestion";
+ info->description =
+ "Test CEL records for a channel that\n"
+ "performs a dial operation to an endpoint that's congested\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ CREATE_ALICE_CHANNEL(chan_caller, &caller);
+
+ EMULATE_DIAL(chan_caller, CHANNEL_TECH_NAME "/Bob");
+
+ START_DIALED(chan_caller, chan_callee);
+
+ ast_channel_state_set(chan_caller, AST_STATE_RINGING);
+ ast_channel_publish_dial(chan_caller, chan_callee, NULL, "CONGESTION");
+
+ HANGUP_CHANNEL(chan_caller, AST_CAUSE_CONGESTION, "34,,CONGESTION");
+ HANGUP_CHANNEL(chan_callee, AST_CAUSE_CONGESTION, "34,,");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_cel_dial_unavailable)
+{
+ RAII_VAR(struct ast_channel *, chan_caller, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_callee, NULL, safe_channel_release);
+ struct ast_party_caller caller = ALICE_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test CEL for a dial that results in unavailable";
+ info->description =
+ "Test CEL records for a channel that\n"
+ "performs a dial operation to an endpoint that's unavailable\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ CREATE_ALICE_CHANNEL(chan_caller, &caller);
+
+ EMULATE_DIAL(chan_caller, CHANNEL_TECH_NAME "/Bob");
+
+ START_DIALED(chan_caller, chan_callee);
+
+ ast_channel_state_set(chan_caller, AST_STATE_RINGING);
+ ast_channel_publish_dial(chan_caller, chan_callee, NULL, "CHANUNAVAIL");
+
+ HANGUP_CHANNEL(chan_caller, AST_CAUSE_NO_ROUTE_DESTINATION, "3,,CHANUNAVAIL");
+ HANGUP_CHANNEL(chan_callee, AST_CAUSE_NO_ROUTE_DESTINATION, "3,,");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_cel_dial_caller_cancel)
+{
+ RAII_VAR(struct ast_channel *, chan_caller, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_callee, NULL, safe_channel_release);
+ struct ast_party_caller caller = ALICE_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test CEL for a dial where the caller cancels";
+ info->description =
+ "Test CEL records for a channel that\n"
+ "performs a dial operation to an endpoint but then decides\n"
+ "to hang up, cancelling the dial\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ CREATE_ALICE_CHANNEL(chan_caller, &caller);
+
+ EMULATE_DIAL(chan_caller, CHANNEL_TECH_NAME "/Bob");
+
+ START_DIALED(chan_caller, chan_callee);
+
+ ast_channel_state_set(chan_caller, AST_STATE_RINGING);
+ ast_channel_publish_dial(chan_caller, chan_callee, NULL, "CANCEL");
+
+ HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "16,,");
+ HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "16,,CANCEL");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_cel_dial_parallel_failed)
+{
+ RAII_VAR(struct ast_channel *, chan_caller, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_bob, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_charlie, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_david, NULL, safe_channel_release);
+ struct ast_party_caller caller = ALICE_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test a parallel dial where all channels fail to answer";
+ info->description =
+ "This tests dialing three parties: Bob, Charlie, David. Charlie\n"
+ "returns BUSY; David returns CONGESTION; Bob fails to answer and\n"
+ "Alice hangs up. Three records are created for Alice as a result.\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ CREATE_ALICE_CHANNEL(chan_caller, &caller);
+
+ /* Channel enters Dial app */
+ EMULATE_DIAL(chan_caller, CHANNEL_TECH_NAME "/Bob&" CHANNEL_TECH_NAME "/Charlie&" CHANNEL_TECH_NAME "/David");
+
+ /* Outbound channels are created */
+ START_DIALED_FULL(chan_caller, chan_bob, "200", "Bob");
+ START_DIALED_FULL(chan_caller, chan_charlie, "300", "Charlie");
+ START_DIALED_FULL(chan_caller, chan_david, "400", "David");
+
+ /* Dial starts */
+ ast_channel_state_set(chan_caller, AST_STATE_RINGING);
+
+ /* Charlie is busy */
+ ast_channel_publish_dial(chan_caller, chan_charlie, NULL, "BUSY");
+ HANGUP_CHANNEL(chan_charlie, AST_CAUSE_BUSY, "17,,");
+
+ /* David is congested */
+ ast_channel_publish_dial(chan_caller, chan_david, NULL, "CONGESTION");
+ HANGUP_CHANNEL(chan_david, AST_CAUSE_CONGESTION, "34,,");
+
+ /* Bob is canceled */
+ ast_channel_publish_dial(chan_caller, chan_bob, NULL, "CANCEL");
+ HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "16,,");
+
+ /* Alice hangs up */
+ HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "16,,BUSY");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_cel_dial_answer_no_bridge)
+{
+ RAII_VAR(struct ast_channel *, chan_caller, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_callee, NULL, safe_channel_release);
+ struct ast_party_caller caller = ALICE_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test dialing, answering, and not going into a bridge.";
+ info->description =
+ "This is a weird one, but theoretically possible. You can perform\n"
+ "a dial, then bounce both channels to different priorities and\n"
+ "never have them enter a bridge together. Ew. This makes sure that\n"
+ "when we answer, we get a CEL, it gets ended at that point, and\n"
+ "that it gets finalized appropriately.\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ CREATE_ALICE_CHANNEL(chan_caller, &caller);
+
+ EMULATE_DIAL(chan_caller, CHANNEL_TECH_NAME "/Bob");
+
+ START_DIALED(chan_caller, chan_callee);
+
+ ast_channel_state_set(chan_caller, AST_STATE_RINGING);
+ ast_channel_publish_dial(chan_caller, chan_callee, NULL, "ANSWER");
+
+ ANSWER_NO_APP(chan_caller);
+ ast_clear_flag(ast_channel_flags(chan_callee), AST_FLAG_OUTGOING);
+ ANSWER_NO_APP(chan_callee);
+
+ EMULATE_APP_DATA(chan_caller, 2, "Wait", "1");
+ EMULATE_APP_DATA(chan_callee, 1, "Wait", "1");
+
+ HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "16,,ANSWER");
+ HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "16,,");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_cel_dial_answer_twoparty_bridge_a)
+{
+ RAII_VAR(struct ast_channel *, chan_caller, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_callee, NULL, safe_channel_release);
+ RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
+ struct ast_party_caller caller = ALICE_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test dialing, answering, and going into a 2-party bridge";
+ info->description =
+ "The most 'basic' of scenarios\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+ bridge = ast_bridge_basic_new();
+ ast_test_validate(test, bridge != NULL);
+
+ CREATE_ALICE_CHANNEL(chan_caller, &caller);
+
+ EMULATE_DIAL(chan_caller, CHANNEL_TECH_NAME "/Bob");
+
+ START_DIALED(chan_caller, chan_callee);
+
+ ast_channel_state_set(chan_caller, AST_STATE_RINGING);
+ ast_channel_publish_dial(chan_caller, chan_callee, NULL, "ANSWER");
+
+ ANSWER_NO_APP(chan_caller);
+ ANSWER_NO_APP(chan_callee);
+
+ do_sleep();
+
+ ast_bridge_impart(bridge, chan_caller, NULL, NULL, 0);
+ do_sleep();
+ ast_bridge_impart(bridge, chan_callee, NULL, NULL, 0);
+ do_sleep();
+ APPEND_EVENT(chan_caller, AST_CEL_BRIDGE_START, NULL, NULL, ast_channel_name(chan_callee));
+
+ ast_bridge_depart(chan_caller);
+ ast_bridge_depart(chan_callee);
+ APPEND_EVENT(chan_caller, AST_CEL_BRIDGE_END, NULL, NULL, ast_channel_name(chan_callee));
+
+ HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "16,,ANSWER");
+ HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "16,,");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_cel_dial_answer_twoparty_bridge_b)
+{
+ RAII_VAR(struct ast_channel *, chan_caller, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_callee, NULL, safe_channel_release);
+ RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
+ struct ast_party_caller caller = ALICE_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test dialing, answering, and going into a 2-party bridge";
+ info->description =
+ "The most 'basic' of scenarios\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+ bridge = ast_bridge_basic_new();
+ ast_test_validate(test, bridge != NULL);
+
+ CREATE_ALICE_CHANNEL(chan_caller, &caller);
+
+ EMULATE_DIAL(chan_caller, CHANNEL_TECH_NAME "/Bob");
+
+ START_DIALED(chan_caller, chan_callee);
+
+ ast_channel_state_set(chan_caller, AST_STATE_RINGING);
+ ast_channel_publish_dial(chan_caller, chan_callee, NULL, "ANSWER");
+
+ ANSWER_NO_APP(chan_caller);
+ ANSWER_NO_APP(chan_callee);
+
+ do_sleep();
+ ast_bridge_impart(bridge, chan_callee, NULL, NULL, 0);
+ do_sleep();
+ ast_bridge_impart(bridge, chan_caller, NULL, NULL, 0);
+ do_sleep();
+ APPEND_EVENT(chan_callee, AST_CEL_BRIDGE_START, NULL, NULL, ast_channel_name(chan_caller));
+
+ ast_bridge_depart(chan_caller);
+ ast_bridge_depart(chan_callee);
+ APPEND_EVENT(chan_callee, AST_CEL_BRIDGE_END, NULL, NULL, ast_channel_name(chan_caller));
+
+ HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "16,,ANSWER");
+ HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "16,,");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_cel_dial_answer_multiparty)
+{
+ RAII_VAR(struct ast_channel *, chan_alice, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_bob, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_charlie, NULL, safe_channel_release);
+ RAII_VAR(struct ast_channel *, chan_david, NULL, safe_channel_release);
+ RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
+ struct ast_party_caller alice_caller = ALICE_CALLERID;
+ struct ast_party_caller charlie_caller = CHARLIE_CALLERID;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = TEST_CATEGORY;
+ info->summary = "Test dialing, answering, and going into a multi-party bridge";
+ info->description =
+ "A little tricky to get to do, but possible with some redirects.\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+ bridge = ast_bridge_basic_new();
+ ast_test_validate(test, bridge != NULL);
+
+ CREATE_ALICE_CHANNEL(chan_alice, &alice_caller);
+
+ EMULATE_DIAL(chan_alice, CHANNEL_TECH_NAME "/Bob");
+
+ START_DIALED(chan_alice, chan_bob);
+
+ CREATE_CHARLIE_CHANNEL(chan_charlie, &charlie_caller);
+ EMULATE_DIAL(chan_charlie, CHANNEL_TECH_NAME "/Bob");
+
+ START_DIALED_FULL(chan_charlie, chan_david, "400", "David");
+
+ ast_channel_state_set(chan_alice, AST_STATE_RINGING);
+ ast_channel_state_set(chan_charlie, AST_STATE_RINGING);
+ ast_channel_publish_dial(chan_alice, chan_bob, NULL, "ANSWER");
+ ast_channel_publish_dial(chan_charlie, chan_david, NULL, "ANSWER");
+
+ ANSWER_NO_APP(chan_alice);
+ ANSWER_NO_APP(chan_bob);
+ ANSWER_NO_APP(chan_charlie);
+ ANSWER_NO_APP(chan_david);
+
+ do_sleep();
+ ast_test_validate(test, 0 == ast_bridge_impart(bridge, chan_charlie, NULL, NULL, 0));
+ do_sleep();
+ ast_test_validate(test, 0 == ast_bridge_impart(bridge, chan_david, NULL, NULL, 0));
+ do_sleep();
+ APPEND_EVENT(chan_charlie, AST_CEL_BRIDGE_START, NULL, NULL, ast_channel_name(chan_david));
+
+ ast_test_validate(test, 0 == ast_bridge_impart(bridge, chan_bob, NULL, NULL, 0));
+ do_sleep();
+ APPEND_EVENT(chan_charlie, AST_CEL_BRIDGE_TO_CONF, NULL, ast_channel_name(chan_bob), ast_channel_name(chan_david));
+
+ ast_test_validate(test, 0 == ast_bridge_impart(bridge, chan_alice, NULL, NULL, 0));
+ do_sleep();
+ APPEND_EVENT(chan_alice, AST_CEL_CONF_ENTER, NULL, NULL, NULL);
+
+ ast_test_validate(test, 0 == ast_bridge_depart(chan_alice));
+ APPEND_EVENT(chan_alice, AST_CEL_CONF_EXIT, NULL, NULL, NULL);
+
+ ast_test_validate(test, 0 == ast_bridge_depart(chan_bob));
+ APPEND_EVENT(chan_bob, AST_CEL_CONF_EXIT, NULL, NULL, NULL);
+
+ ast_test_validate(test, 0 == ast_bridge_depart(chan_charlie));
+ APPEND_EVENT(chan_charlie, AST_CEL_CONF_EXIT, NULL, NULL, NULL);
+
+ ast_test_validate(test, 0 == ast_bridge_depart(chan_david));
+ APPEND_EVENT(chan_david, AST_CEL_CONF_EXIT, NULL, NULL, NULL);
+
+ HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "16,,ANSWER");
+ HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "16,,");
+ HANGUP_CHANNEL(chan_charlie, AST_CAUSE_NORMAL, "16,,ANSWER");
+ HANGUP_CHANNEL(chan_david, AST_CAUSE_NORMAL, "16,,");
+
+ return AST_TEST_PASS;
+}
+
+/*! Subscription for CEL events */
+static struct ast_event_sub *event_sub = NULL;
+
+/*! Container for astobj2 duplicated ast_events */
+static struct ao2_container *cel_received_events = NULL;
+
+/*! Container for expected CEL events */
+static struct ao2_container *cel_expected_events = NULL;
+
+static struct ast_event *ao2_dup_event(const struct ast_event *event)
+{
+ struct ast_event *event_dup;
+ uint16_t event_len;
+
+ event_len = ast_event_get_size(event);
+
+ event_dup = ao2_alloc(event_len, NULL);
+ if (!event_dup) {
+ return NULL;
+ }
+
+ memcpy(event_dup, event, event_len);
+
+ return event_dup;
+}
+
+static int append_expected_event(
+ struct ast_channel *chan,
+ enum ast_cel_event_type type,
+ const char *userdefevname,
+ const char *extra, const char *peer)
+{
+ RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_event *, ev, NULL, ast_free);
+ RAII_VAR(struct ast_event *, ao2_ev, NULL, ao2_cleanup);
+ snapshot = ast_channel_snapshot_create(chan);
+ if (!snapshot) {
+ return -1;
+ }
+
+ ev = ast_cel_create_event(snapshot, type, userdefevname, extra, peer);
+ if (!ev) {
+ return -1;
+ }
+
+ ao2_ev = ao2_dup_event(ev);
+ if (!ao2_ev) {
+ return -1;
+ }
+
+ ao2_link(cel_expected_events, ao2_ev);
+ return 0;
+}
+
+ast_mutex_t sync_lock;
+ast_cond_t sync_out;
+
+static void test_sub(const struct ast_event *event, void *data)
+{
[... 395 lines stripped ...]
More information about the asterisk-commits
mailing list