Skip to content
Snippets Groups Projects
test_cel.c 67.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	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, "");
    
    
    	/* David is congested */
    	ast_channel_publish_dial(chan_caller, chan_david, NULL, "CONGESTION");
    
    	HANGUP_CHANNEL(chan_david, AST_CAUSE_CONGESTION, "");
    
    
    	/* Bob is canceled */
    	ast_channel_publish_dial(chan_caller, chan_bob, NULL, "CANCEL");
    
    	HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "");
    
    
    	/* Alice hangs up */
    
    	HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "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.";
    
    		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, "ANSWER");
    	HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "");
    
    
    	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, safe_bridge_destroy);
    
    	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";
    
    		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();
    
    
    	BRIDGE_ENTER(chan_caller, bridge);
    	BRIDGE_ENTER(chan_callee, bridge);
    
    	BRIDGE_EXIT(chan_caller, bridge);
    	BRIDGE_EXIT(chan_callee, bridge);
    
    	HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "ANSWER");
    	HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "");
    
    
    	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, safe_bridge_destroy);
    
    	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";
    
    		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();
    
    	BRIDGE_ENTER(chan_callee, bridge);
    	BRIDGE_ENTER(chan_caller, bridge);
    
    	BRIDGE_EXIT(chan_caller, bridge);
    	BRIDGE_EXIT(chan_callee, bridge);
    
    	HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "ANSWER");
    	HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "");
    
    #ifdef RACEY_TESTS
    
    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, safe_bridge_destroy);
    
    	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.";
    
    		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);
    
    	do_sleep();
    
    
    	CREATE_CHARLIE_CHANNEL(chan_charlie, &charlie_caller);
    
    	do_sleep();
    
    	EMULATE_DIAL(chan_charlie, CHANNEL_TECH_NAME "/Bob");
    
    	do_sleep();
    
    
    	START_DIALED_FULL(chan_charlie, chan_david, "400", "David");
    
    	ast_channel_state_set(chan_alice, AST_STATE_RINGING);
    
    	do_sleep();
    
    	ast_channel_state_set(chan_charlie, AST_STATE_RINGING);
    
    	do_sleep();
    
    	ast_channel_publish_dial(chan_alice, chan_bob, NULL, "ANSWER");
    
    	do_sleep();
    
    	ast_channel_publish_dial(chan_charlie, chan_david, NULL, "ANSWER");
    
    	do_sleep();
    
    
    	ANSWER_NO_APP(chan_alice);
    
    	do_sleep();
    
    	ANSWER_NO_APP(chan_bob);
    
    	do_sleep();
    
    	ANSWER_NO_APP(chan_charlie);
    
    	do_sleep();
    
    	ANSWER_NO_APP(chan_david);
    
    	do_sleep();
    
    	BRIDGE_ENTER(chan_charlie, bridge);
    	BRIDGE_ENTER(chan_david, bridge);
    	BRIDGE_ENTER(chan_bob, bridge);
    	BRIDGE_ENTER(chan_alice, bridge);
    
    	BRIDGE_EXIT(chan_alice, bridge);
    	BRIDGE_EXIT(chan_bob, bridge);
    	BRIDGE_EXIT(chan_charlie, bridge);
    	BRIDGE_EXIT(chan_david, bridge);
    
    
    	HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "ANSWER");
    	HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "");
    	HANGUP_CHANNEL(chan_charlie, AST_CAUSE_NORMAL, "ANSWER");
    	HANGUP_CHANNEL(chan_david, AST_CAUSE_NORMAL, "");
    
    	return AST_TEST_PASS;
    }
    
    
    AST_TEST_DEFINE(test_cel_blind_transfer)
    {
    	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, safe_bridge_destroy);
    
    	RAII_VAR(struct ast_blind_transfer_message *, transfer_msg, NULL, ao2_cleanup);
    
    	struct ast_party_caller alice_caller = ALICE_CALLERID;
    	struct ast_party_caller bob_caller = BOB_CALLERID;
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = __func__;
    		info->category = TEST_CATEGORY;
    		info->summary = "Test blind transfers to an extension";
    		info->description =
    			"This test creates two channels, bridges them, and then"
    
    			" blind transfers the bridge to an extension.";
    
    		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);
    	CREATE_BOB_CHANNEL(chan_bob, &bob_caller);
    
    	ANSWER_NO_APP(chan_alice);
    	ANSWER_NO_APP(chan_bob);
    
    
    	BRIDGE_ENTER(chan_bob, bridge);
    	BRIDGE_ENTER(chan_alice, bridge);
    
    	ast_bridge_lock(bridge);
    
    	transfer_msg = ast_blind_transfer_message_create(1, chan_alice,
    			"transfer_extension", "transfer_context");
    	if (!transfer_msg) {
    
    		ast_bridge_unlock(bridge);
    
    		ast_test_status_update(test, "Failed to create transfer Stasis message\n");
    		return AST_TEST_FAIL;
    	}
    	transfer_msg->bridge = ast_bridge_snapshot_create(bridge);
    	if (!transfer_msg->bridge) {
    
    		ast_bridge_unlock(bridge);
    
    		ast_test_status_update(test, "Failed to create bridge snapshot\n");
    		return AST_TEST_FAIL;
    	}
    
    	ast_bridge_unlock(bridge);
    
    	transfer_msg->result = AST_BRIDGE_TRANSFER_SUCCESS;
    	ast_bridge_publish_blind_transfer(transfer_msg);
    
    	BLINDTRANSFER_EVENT(chan_alice, bridge, "transfer_extension", "transfer_context");
    
    
    	BRIDGE_EXIT(chan_alice, bridge);
    	BRIDGE_EXIT(chan_bob, bridge);
    
    	HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "");
    
    	HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "");
    
    	return AST_TEST_PASS;
    }
    
    
    /* XXX Validation needs to take into account the BRIDGE_EXIT for Alice and the
     * ATTENDEDTRANSFER message are not guaranteed to be ordered
     */
    #ifdef RACEY_TESTS
    
    AST_TEST_DEFINE(test_cel_attended_transfer_bridges_swap)
    {
    	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 *, bridge1, NULL, safe_bridge_destroy);
    	RAII_VAR(struct ast_bridge *, bridge2, NULL, safe_bridge_destroy);
    
    	struct ast_party_caller alice_caller = ALICE_CALLERID;
    	struct ast_party_caller bob_caller = BOB_CALLERID;
    	struct ast_party_caller charlie_caller = CHARLIE_CALLERID;
    
    	struct ast_party_caller david_caller = ALICE_CALLERID;
    
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = __func__;
    		info->category = TEST_CATEGORY;
    		info->summary = "Test attended transfers between two pairs of bridged parties";
    		info->description =
    			"This test creates four channels, places each pair in"
    			" a bridge, and then attended transfers the bridges"
    
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    	/* Create first set of bridged parties */
    	bridge1 = ast_bridge_basic_new();
    	ast_test_validate(test, bridge1 != NULL);
    
    	CREATE_ALICE_CHANNEL(chan_alice, &alice_caller);
    	CREATE_BOB_CHANNEL(chan_bob, &bob_caller);
    	ANSWER_NO_APP(chan_alice);
    	ANSWER_NO_APP(chan_bob);
    
    
    	BRIDGE_ENTER(chan_bob, bridge1);
    	BRIDGE_ENTER(chan_alice, bridge1);
    
    
    	/* Create second set of bridged parties */
    	bridge2 = ast_bridge_basic_new();
    	ast_test_validate(test, bridge2 != NULL);
    
    
    	CREATE_DAVID_CHANNEL(chan_david, &david_caller);
    
    	CREATE_CHARLIE_CHANNEL(chan_charlie, &charlie_caller);
    
    	ANSWER_NO_APP(chan_david);
    
    	ANSWER_NO_APP(chan_charlie);
    
    
    	BRIDGE_ENTER(chan_charlie, bridge2);
    
    	BRIDGE_ENTER(chan_david, bridge2);
    	BRIDGE_EXIT_EVENT(chan_bob, bridge1);
    
    	do_sleep();
    
    	/* Perform attended transfer */
    
    	if (ast_bridge_transfer_attended(chan_alice, chan_david)) {
    		ast_test_status_update(test, "Attended transfer failed!\n");
    		return AST_TEST_FAIL;
    	}
    
    	do_sleep();
    
    	BRIDGE_ENTER_EVENT_PEER(chan_bob, bridge2, "CELTestChannel/David,CELTestChannel/Charlie");
    
    	BRIDGE_EXIT_EVENT(chan_david, bridge2);
    	BRIDGE_EXIT_EVENT(chan_alice, bridge1);
    
    	ATTENDEDTRANSFER_BRIDGE(chan_alice, bridge1, chan_david, bridge2, chan_charlie, chan_bob);
    
    	BRIDGE_EXIT(chan_bob, bridge2);
    	BRIDGE_EXIT(chan_charlie, bridge2);
    
    
    	HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "");
    
    	HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "");
    
    	HANGUP_CHANNEL(chan_david, AST_CAUSE_NORMAL, "");
    
    	HANGUP_CHANNEL(chan_charlie, AST_CAUSE_NORMAL, "");
    
    	return AST_TEST_PASS;
    }
    
    
    AST_TEST_DEFINE(test_cel_attended_transfer_bridges_merge)
    {
    	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 *, bridge1, NULL, safe_bridge_destroy);
    	RAII_VAR(struct ast_bridge *, bridge2, NULL, safe_bridge_destroy);
    
    	struct ast_party_caller alice_caller = ALICE_CALLERID;
    	struct ast_party_caller bob_caller = BOB_CALLERID;
    	struct ast_party_caller charlie_caller = CHARLIE_CALLERID;
    
    	struct ast_party_caller david_caller = ALICE_CALLERID;
    
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = __func__;
    		info->category = TEST_CATEGORY;
    		info->summary = "Test attended transfers between two pairs of"
    			" bridged parties that results in a bridge merge";
    		info->description =
    
    			"This test creates four channels, places each pair"
    
    			" in a bridge, and then attended transfers the bridges"
    
    			" together causing a bridge merge.";
    
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    	/* Create first set of bridged parties */
    
    	bridge1 = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_MULTIMIX,
    
    		AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED | AST_BRIDGE_FLAG_SMART,
    
    		"test_cel", "test_cel_atxfer_bridges_merge_1", NULL);
    
    	ast_test_validate(test, bridge1 != NULL);
    
    	CREATE_ALICE_CHANNEL(chan_alice, &alice_caller);
    	CREATE_BOB_CHANNEL(chan_bob, &bob_caller);
    	ANSWER_NO_APP(chan_alice);
    	ANSWER_NO_APP(chan_bob);
    
    
    	BRIDGE_ENTER(chan_bob, bridge1);
    	BRIDGE_ENTER(chan_alice, bridge1);
    
    
    	/* Create second set of bridged parties */
    
    	bridge2 = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_MULTIMIX,
    
    		AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED | AST_BRIDGE_FLAG_SMART,
    
    		"test_cel", "test_cel_atxfer_bridges_merge_2", NULL);
    
    	ast_test_validate(test, bridge2 != NULL);
    
    
    	CREATE_DAVID_CHANNEL(chan_david, &david_caller);
    
    	CREATE_CHARLIE_CHANNEL(chan_charlie, &charlie_caller);
    
    	ANSWER_NO_APP(chan_david);
    
    	ANSWER_NO_APP(chan_charlie);
    
    
    	BRIDGE_ENTER(chan_charlie, bridge2);
    
    	BRIDGE_ENTER(chan_david, bridge2);
    
    
    	/* Perform attended transfer */
    
    	if (ast_bridge_transfer_attended(chan_alice, chan_david)) {
    		ast_test_status_update(test, "Attended transfer failed!\n");
    		return AST_TEST_FAIL;
    	}
    
    	BRIDGE_EXIT_EVENT_PEER(chan_charlie, bridge2, "CELTestChannel/David");
    	BRIDGE_ENTER_EVENT_PEER(chan_charlie, bridge1, "CELTestChannel/Bob,CELTestChannel/Alice");
    
    	BRIDGE_EXIT_EVENT(chan_david, bridge2);
    	BRIDGE_EXIT_EVENT(chan_alice, bridge1);
    
    	ATTENDEDTRANSFER_BRIDGE(chan_alice, bridge1, chan_david, bridge2, chan_charlie, chan_bob);
    
    	BRIDGE_EXIT(chan_bob, bridge1);
    	BRIDGE_EXIT(chan_charlie, bridge1);
    
    
    	HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "");
    
    	HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "");
    
    	HANGUP_CHANNEL(chan_david, AST_CAUSE_NORMAL, "");
    
    	HANGUP_CHANNEL(chan_charlie, AST_CAUSE_NORMAL, "");
    
    
    	return AST_TEST_PASS;
    }
    
    
    /* XXX Validation needs to take into account the BRIDGE_EXIT for David and the
     * ATTENDEDTRANSFER message are not guaranteed to be ordered
     */
    #ifdef RACEY_TESTS
    
    AST_TEST_DEFINE(test_cel_attended_transfer_bridges_link)
    {
    	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 *, bridge1, NULL, safe_bridge_destroy);
    	RAII_VAR(struct ast_bridge *, bridge2, NULL, safe_bridge_destroy);
    
    	struct ast_party_caller alice_caller = ALICE_CALLERID;
    	struct ast_party_caller bob_caller = BOB_CALLERID;
    	struct ast_party_caller charlie_caller = CHARLIE_CALLERID;
    
    	struct ast_party_caller david_caller = ALICE_CALLERID;
    
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = __func__;
    		info->category = TEST_CATEGORY;
    		info->summary = "Test attended transfers between two pairs of"
    			" bridged parties that results in a bridge merge";
    		info->description =
    
    			"This test creates four channels, places each pair"
    
    			" in a bridge, and then attended transfers the bridges"
    
    			" together causing a bridge link.";
    
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    	/* Create first set of bridged parties */
    	bridge1 = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_MULTIMIX,
    		AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
    
    		| AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM
    
    		| AST_BRIDGE_FLAG_TRANSFER_PROHIBITED | AST_BRIDGE_FLAG_SMART,
    
    		"test_cel", "test_cel_atxfer_bridges_link_1", NULL);
    
    	ast_test_validate(test, bridge1 != NULL);
    
    	CREATE_ALICE_CHANNEL(chan_alice, &alice_caller);
    	CREATE_BOB_CHANNEL(chan_bob, &bob_caller);
    	ANSWER_NO_APP(chan_alice);
    	ANSWER_NO_APP(chan_bob);
    
    
    	BRIDGE_ENTER(chan_bob, bridge1);
    	BRIDGE_ENTER(chan_alice, bridge1);
    
    
    	/* Create second set of bridged parties */
    
    	bridge2 = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_MULTIMIX,
    		AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
    		| AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM
    
    		| AST_BRIDGE_FLAG_TRANSFER_PROHIBITED | AST_BRIDGE_FLAG_SMART,
    
    		"test_cel", "test_cel_atxfer_bridges_link_2", NULL);
    
    	ast_test_validate(test, bridge2 != NULL);
    
    
    	CREATE_DAVID_CHANNEL(chan_david, &david_caller);
    
    	CREATE_CHARLIE_CHANNEL(chan_charlie, &charlie_caller);
    
    	ANSWER_NO_APP(chan_david);
    
    	ANSWER_NO_APP(chan_charlie);
    
    
    	BRIDGE_ENTER(chan_charlie, bridge2);
    	BRIDGE_ENTER(chan_david, bridge2);
    
    
    	/* Perform attended transfer */
    
    	ATTENDEDTRANSFER_BRIDGE(chan_alice, bridge1, chan_david, bridge2, chan_charlie, chan_bob);
    
    	ast_bridge_transfer_attended(chan_alice, chan_david);
    	do_sleep();
    
    	/* BRIDGE_EXIT alice and david */
    	APPEND_DUMMY_EVENT();
    
    	APPEND_DUMMY_EVENT();
    
    
    	do_sleep();
    
    	BRIDGE_EXIT(chan_bob, bridge1);
    	BRIDGE_EXIT(chan_charlie, bridge2);
    
    
    	HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "");
    
    	HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "");
    
    	HANGUP_CHANNEL(chan_david, AST_CAUSE_NORMAL, "");
    
    	HANGUP_CHANNEL(chan_charlie, AST_CAUSE_NORMAL, "");
    
    	return AST_TEST_PASS;
    }
    
    
    AST_TEST_DEFINE(test_cel_dial_pickup)
    {
    	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_channel *, chan_charlie, NULL, safe_channel_release);
    	struct ast_party_caller 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 call pickup";
    		info->description =
    			"Test CEL records for a call that is\n"
    			"inbound to Asterisk, executes some dialplan, and\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);
    
    	CREATE_CHARLIE_CHANNEL(chan_charlie, &charlie_caller);
    
    	{
    
    		RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
    
    		SCOPED_CHANNELLOCK(lock, chan_callee);
    
    		extra = ast_json_pack("{s: s, s: s}", "pickup_channel", ast_channel_name(chan_charlie),
    			"pickup_channel_uniqueid", ast_channel_uniqueid(chan_charlie));
    
    		ast_test_validate(test, extra != NULL);
    
    		APPEND_EVENT(chan_callee, AST_CEL_PICKUP, NULL, extra);
    
    		ast_test_validate(test, !ast_do_pickup(chan_charlie, chan_callee));
    
    	}
    
    	/* Hang up the masqueraded zombie */
    	HANGUP_CHANNEL(chan_charlie, AST_CAUSE_NORMAL, "");
    
    	ast_channel_publish_dial(chan_caller, chan_callee, NULL, "ANSWER");
    
    
    	HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "CANCEL");
    
    	HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "");
    
    AST_TEST_DEFINE(test_cel_local_optimize)
    {
    	RAII_VAR(struct ast_channel *, chan_alice, NULL, safe_channel_release);
    	RAII_VAR(struct ast_channel *, chan_bob, NULL, safe_channel_release);
    	struct ast_party_caller alice_caller = ALICE_CALLERID;
    	struct ast_party_caller bob_caller = BOB_CALLERID;
    	RAII_VAR(struct ast_multi_channel_blob *, mc_blob, NULL, ao2_cleanup);
    	RAII_VAR(struct ast_channel_snapshot *, alice_snapshot, NULL, ao2_cleanup);
    	RAII_VAR(struct ast_channel_snapshot *, bob_snapshot, NULL, ao2_cleanup);
    	RAII_VAR(struct stasis_message *, local_opt_begin, NULL, ao2_cleanup);
    	RAII_VAR(struct stasis_message *, local_opt_end, NULL, ao2_cleanup);
    
    	RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
    
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = __func__;
    		info->category = TEST_CATEGORY;
    		info->summary = "Test local channel optimization record generation";
    		info->description =
    			"Test CEL records for two local channels being optimized\n"
    			"out by sending a messages indicating local optimization\n"
    
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    
    	mc_blob = ast_multi_channel_blob_create(ast_json_null());
    	ast_test_validate(test, mc_blob != NULL);
    
    	CREATE_ALICE_CHANNEL(chan_alice, &alice_caller);
    	CREATE_BOB_CHANNEL(chan_bob, &bob_caller);
    
    
    	ast_channel_lock(chan_alice);
    
    	alice_snapshot = ast_channel_snapshot_create(chan_alice);
    
    	ast_channel_unlock(chan_alice);
    
    	ast_test_validate(test, alice_snapshot != NULL);
    
    
    	ast_channel_lock(chan_bob);
    
    	bob_snapshot = ast_channel_snapshot_create(chan_bob);
    
    	ast_channel_unlock(chan_bob);
    
    	ast_test_validate(test, bob_snapshot != NULL);
    
    	ast_multi_channel_blob_add_channel(mc_blob, "1", alice_snapshot);
    	ast_multi_channel_blob_add_channel(mc_blob, "2", bob_snapshot);
    
    	local_opt_begin = stasis_message_create(ast_local_optimization_begin_type(), mc_blob);
    	ast_test_validate(test, local_opt_begin != NULL);
    
    	local_opt_end = stasis_message_create(ast_local_optimization_end_type(), mc_blob);
    	ast_test_validate(test, local_opt_end != NULL);
    
    	stasis_publish(ast_channel_topic(chan_alice), local_opt_begin);
    	stasis_publish(ast_channel_topic(chan_alice), local_opt_end);
    
    	extra = ast_json_pack("{s: s, s: s}", "local_two", bob_snapshot->name,
    		"local_two_uniqueid", bob_snapshot->uniqueid);
    
    	ast_test_validate(test, extra != NULL);
    
    
    	APPEND_EVENT_SNAPSHOT(alice_snapshot, AST_CEL_LOCAL_OPTIMIZE, NULL, extra, NULL);
    
    
    	HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL, "");
    	HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL, "");
    
    	return AST_TEST_PASS;
    }
    
    
    /*! 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 void mid_test_sync(void)
    {
    	ast_mutex_lock(&mid_test_sync_lock);
    	if (ao2_container_count(cel_expected_events) <= ao2_container_count(cel_received_events)) {
    		ast_mutex_unlock(&mid_test_sync_lock);
    		return;
    	}
    
    	do_mid_test_sync = 1;
    	ast_mutex_unlock(&mid_test_sync_lock);
    
    	{
    		struct timeval start = ast_tvnow();
    		struct timespec end = {
    			.tv_sec = start.tv_sec + 15,
    			.tv_nsec = start.tv_usec * 1000
    		};
    
    		SCOPED_MUTEX(lock, &sync_lock);
    		ast_cond_timedwait(&sync_out, &sync_lock, &end);
    	}
    }
    
    
    static int append_event(struct ast_event *ev)
    {
    	RAII_VAR(struct ast_event *, ao2_ev, NULL, ao2_cleanup);
    	ao2_ev = ao2_dup_event(ev);
    	if (!ao2_ev) {
    		return -1;
    	}
    
    	ao2_link(cel_expected_events, ao2_ev);
    	return 0;
    }
    
    
    static int append_dummy_event(void)
    
    {
    	RAII_VAR(struct ast_event *, ev, NULL, ast_free);
    	RAII_VAR(struct ast_event *, ao2_ev, NULL, ao2_cleanup);
    
    
    	ev = ast_event_new(AST_EVENT_CUSTOM, AST_EVENT_IE_END);
    	if (!ev) {
    
    	return append_event(ev);
    }
    
    
    static int append_expected_event_snapshot(
    	struct ast_channel_snapshot *snapshot,
    	enum ast_cel_event_type type,
    	const char *userdefevname,
    
    	struct ast_json *extra,
    	const char *peer)
    
    {
    	RAII_VAR(struct ast_event *, ev, NULL, ast_free);
    
    	ev = ast_cel_create_event(snapshot, type, userdefevname, extra, peer);
    
    	return append_event(ev);
    }
    
    static int append_expected_event(
    	struct ast_channel *chan,
    	enum ast_cel_event_type type,
    	const char *userdefevname,
    
    	struct ast_json *extra,
    	const char *peer)
    
    {
    	RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
    
    	snapshot = ast_channel_snapshot_create(chan);
    
    	if (!snapshot) {
    
    	return append_expected_event_snapshot(snapshot, type, userdefevname, extra, peer);
    
    static void test_sub(struct ast_event *event)
    
    {
    	struct ast_event *event_dup = ao2_dup_event(event);
    
    	SCOPED_MUTEX(mid_test_lock, &mid_test_sync_lock);
    
    
    	if (!event_dup) {
    		return;
    	}
    
    
    	chan_name = ast_event_get_ie_str(event_dup, AST_EVENT_IE_CEL_CHANNAME);
    	if (chan_name && strncmp(chan_name, CHANNEL_TECH_NAME, 14)) {
    		return;
    	}
    
    
    	/* save the event for later processing */
    	ao2_link(cel_received_events, event_dup);
    
    
    	if (do_mid_test_sync) {
    		int expected = ao2_container_count(cel_expected_events);
    		int received = ao2_container_count(cel_received_events);
    		if (expected <= received) {
    			{
    			SCOPED_MUTEX(lock, &sync_lock);
    			ast_cond_signal(&sync_out);
    			do_mid_test_sync = 0;
    			}
    		}
    	}
    
    Richard Mudgett's avatar
    Richard Mudgett committed
     * \internal
     * \brief Callback function called before each test executes
    
     */
    static int test_cel_init_cb(struct ast_test_info *info, struct ast_test *test)
    {
    	ast_assert(cel_received_events == NULL);
    	ast_assert(cel_expected_events == NULL);
    
    
    	ast_mutex_init(&mid_test_sync_lock);
    
    	ast_mutex_init(&sync_lock);
    	ast_cond_init(&sync_out, NULL);
    
    	/* Back up the real CEL config and insert the test's config */
    	saved_config = ast_cel_get_config();
    	ast_cel_set_config(cel_test_config);
    
    	/* init CEL event storage (degenerate hash table becomes a linked list) */
    	cel_received_events = ao2_container_alloc(1, NULL, NULL);
    	cel_expected_events = ao2_container_alloc(1, NULL, NULL);
    
    	/* start the CEL event callback */
    
    	if (ast_cel_backend_register(TEST_BACKEND_NAME, test_sub)) {
    		return -1;
    	}
    
    /*!
     * \brief Check two peer strings for equality
     *
     * \retval zero if the peer strings do not match
     * \retval non-zero if the peer strings match
     */
    static int test_cel_peer_strings_match(const char *str1, const char *str2)
    {
    	struct ao2_container *intersection = ast_str_container_alloc(11);
    	RAII_VAR(char *, str1_dup, ast_strdup(str1), ast_free);
    	RAII_VAR(char *, str2_dup, ast_strdup(str2), ast_free);
    	char *chan;
    
    	while ((chan = strsep(&str1_dup, ","))) {
    		ast_str_container_add(intersection, chan);
    	}
    
    	while ((chan = strsep(&str2_dup, ","))) {
    		RAII_VAR(char *, ao2_chan, ao2_find(intersection, chan, OBJ_SEARCH_KEY), ao2_cleanup);
    
    		/* item in str2 not in str1 */
    		if (!ao2_chan) {
    			return 0;
    		}
    
    		ast_str_container_remove(intersection, chan);
    	}
    
    	/* item in str1 not in str2 */
    	if (ao2_container_count(intersection)) {
    		return 0;
    	}
    
    	return 1;
    }
    
    /*!
     * \brief Check an IE value from two events
     *
     * \retval zero if the IEs in the events of the specified type do not match
     * \retval non-zero if the IEs in the events of the specified type match
     */
    
    static int match_ie_val(
    	const struct ast_event *event1,
    	const struct ast_event *event2,
    	enum ast_event_ie_type type)
    {
    	enum ast_event_ie_pltype pltype = ast_event_get_ie_pltype(type);
    
    
    	/* XXX ignore sec/usec for now */
    	if (type == AST_EVENT_IE_CEL_EVENT_TIME_USEC) {
    		return 1;
    	}
    
    	if (type == AST_EVENT_IE_CEL_EVENT_TIME) {
    		return 1;
    	}
    
    
    	switch (pltype) {
    	case AST_EVENT_IE_PLTYPE_UINT:
    	{
    		uint32_t val = ast_event_get_ie_uint(event2, type);
    
    		return (val == ast_event_get_ie_uint(event1, type)) ? 1 : 0;
    	}
    	case AST_EVENT_IE_PLTYPE_STR:
    	{
    
    		const char *str1 = ast_event_get_ie_str(event1, type);
    		const char *str2 = ast_event_get_ie_str(event2, type);
    
    		if (!str1 && !str2) {
    			return 1;
    		} else if (!str1) {
    			return 0;
    		} else if (!str2) {
    			return 0;
    		}
    
    		/* use special matching for CEL PEER field */
    		if (type == AST_EVENT_IE_CEL_PEER) {
    			return test_cel_peer_strings_match(str1, str2);
    
    		return !strcmp(str1, str2);
    
    	case AST_EVENT_IE_PLTYPE_RAW:
    	case AST_EVENT_IE_PLTYPE_BITFLAGS:
    		/* Fall through: just pass on these types */
    		return 1;
    
    static int events_are_equal(struct ast_test *test, struct ast_event *received, struct ast_event *expected)
    
    {
    	struct ast_event_iterator iterator;
    	int res;
    
    
    	if (ast_event_get_type(expected) == AST_EVENT_CUSTOM) {
    		/* this event is flagged as a wildcard match */
    		return 1;
    	}
    
    	for (res = ast_event_iterator_init(&iterator, received); !res; res = ast_event_iterator_next(&iterator)) {
    
    		int ie_type = ast_event_iterator_get_ie_type(&iterator);
    
    		if (!match_ie_val(received, expected, ie_type)) {
    
    			ast_test_status_update(test, "Failed matching on field %s\n", ast_event_get_ie_type_name(ie_type));
    
    static int dump_event(struct ast_test *test, struct ast_event *event)
    
    {
    	struct ast_event_iterator i;
    
    	if (ast_event_iterator_init(&i, event)) {
    
    		ast_test_status_update(test, "Failed to initialize event iterator.  :-(\n");
    
    	ast_test_status_update(test, "Event: %s\n",
    
    		ast_cel_get_type_name(ast_event_get_ie_uint(event, AST_EVENT_IE_CEL_EVENT_TYPE)));
    
    	do {
    		enum ast_event_ie_type ie_type;
    		enum ast_event_ie_pltype ie_pltype;
    		const char *ie_type_name;
    
    		ie_type = ast_event_iterator_get_ie_type(&i);
    		ie_type_name = ast_event_get_ie_type_name(ie_type);
    		ie_pltype = ast_event_get_ie_pltype(ie_type);
    
    		switch (ie_pltype) {
    		case AST_EVENT_IE_PLTYPE_UNKNOWN:
    		case AST_EVENT_IE_PLTYPE_STR:
    
    			ast_test_status_update(test, "%.30s: %s\n", ie_type_name,
    
    					ast_event_iterator_get_ie_str(&i));
    			break;
    		case AST_EVENT_IE_PLTYPE_UINT:
    
    			ast_test_status_update(test, "%.30s: %u\n", ie_type_name,
    
    					ast_event_iterator_get_ie_uint(&i));
    			break;
    		default:
    			break;