Skip to content
Snippets Groups Projects
test_cdr.c 89.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL);
    	HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL);
    
    	result = verify_mock_cdr_record(test, &alice_expected, 2);
    
    	return result;
    }
    
    AST_TEST_DEFINE(test_cdr_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, safe_bridge_destroy);
    
    	RAII_VAR(struct ast_cdr_config *, config, ao2_alloc(sizeof(*config), NULL),
    			ao2_cleanup);
    	struct timespec to_sleep = {1, 0};
    
    	struct ast_party_caller caller_alice = ALICE_CALLERID;
    	struct ast_party_caller caller_bob = BOB_CALLERID;
    	struct ast_party_caller caller_charlie = CHARLIE_CALLERID;
    	struct ast_cdr charlie_expected = {
    		.clid = "\"Charlie\" <300>",
    		.src = "300",
    		.dst = "300",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Charlie",
    		.lastapp = "Bridge",
    		.billsec = 1,
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.disposition = AST_CDR_ANSWERED,
    		.accountcode = "300",
    	};
    	struct ast_cdr bob_expected = {
    		.clid = "\"Bob\" <200>",
    		.src = "200",
    		.dst = "200",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Bob",
    		.dstchannel = CHANNEL_TECH_NAME "/Charlie",
    		.lastapp = "Bridge",
    		.billsec = 1,
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.disposition = AST_CDR_ANSWERED,
    		.accountcode = "200",
    		.peeraccount = "300",
    		.next = &charlie_expected,
    	};
    	struct ast_cdr alice_expected_two = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    		.dstchannel = CHANNEL_TECH_NAME "/Charlie",
    		.lastapp = "Bridge",
    		.billsec = 1,
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.disposition = AST_CDR_ANSWERED,
    		.accountcode = "100",
    		.peeraccount = "300",
    		.next = &bob_expected,
    	};
    	struct ast_cdr alice_expected_one = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    		.dstchannel = CHANNEL_TECH_NAME "/Bob",
    		.lastapp = "Bridge",
    		.billsec = 1,
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.disposition = AST_CDR_ANSWERED,
    		.accountcode = "100",
    		.peeraccount = "200",
    		.next = &alice_expected_two,
    	};
    
    	enum ast_test_result_state result = AST_TEST_NOT_RUN;
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = __func__;
    		info->category = TEST_CATEGORY;
    		info->summary = "Test cdrs for a single party entering/leaving a multi-party bridge";
    		info->description =
    			"Test the properties of a CDR for a call that is\n"
    			"answered, enters a bridge, and leaves it. A total of three\n"
    
    			"parties perform this action.";
    
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    	SWAP_CONFIG(config, debug_cdr_config);
    	CREATE_ALICE_CHANNEL(chan_alice, &caller_alice, &alice_expected_one);
    	COPY_IDS(chan_alice, &alice_expected_two);
    	CREATE_BOB_CHANNEL(chan_bob, &caller_bob, &bob_expected);
    	ast_copy_string(bob_expected.linkedid, ast_channel_linkedid(chan_alice), sizeof(bob_expected.linkedid));
    	CREATE_CHARLIE_CHANNEL(chan_charlie, &caller_charlie, &charlie_expected);
    	ast_copy_string(charlie_expected.linkedid, ast_channel_linkedid(chan_alice), sizeof(charlie_expected.linkedid));
    
    
    	ast_channel_lock(chan_alice);
    
    	EMULATE_APP_DATA(chan_alice, 1, "Answer", "");
    	ast_setstate(chan_alice, AST_STATE_UP);
    	EMULATE_APP_DATA(chan_alice, 2, "Bridge", "");
    
    	ast_channel_unlock(chan_alice);
    
    
    	bridge = ast_bridge_basic_new();
    	ast_test_validate(test, bridge != NULL);
    
    	ast_test_validate(test, !ast_bridge_impart(bridge, chan_alice, NULL, NULL, AST_BRIDGE_IMPART_CHAN_DEPARTABLE));
    
    	ast_channel_lock(chan_bob);
    
    	EMULATE_APP_DATA(chan_bob, 1, "Answer", "");
    	ast_setstate(chan_bob, AST_STATE_UP);
    	EMULATE_APP_DATA(chan_bob, 2, "Bridge", "");
    
    	ast_channel_unlock(chan_bob);
    
    	ast_test_validate(test, !ast_bridge_impart(bridge, chan_bob, NULL, NULL, AST_BRIDGE_IMPART_CHAN_DEPARTABLE));
    
    	ast_channel_lock(chan_charlie);
    
    	EMULATE_APP_DATA(chan_charlie, 1, "Answer", "");
    	ast_setstate(chan_charlie, AST_STATE_UP);
    	EMULATE_APP_DATA(chan_charlie, 2, "Bridge", "");
    
    	ast_channel_unlock(chan_charlie);
    
    	ast_test_validate(test, !ast_bridge_impart(bridge, chan_charlie, NULL, NULL, AST_BRIDGE_IMPART_CHAN_DEPARTABLE));
    
    
    	ast_bridge_depart(chan_alice);
    	ast_bridge_depart(chan_bob);
    	ast_bridge_depart(chan_charlie);
    
    	HANGUP_CHANNEL(chan_alice, AST_CAUSE_NORMAL);
    	HANGUP_CHANNEL(chan_bob, AST_CAUSE_NORMAL);
    	HANGUP_CHANNEL(chan_charlie, AST_CAUSE_NORMAL);
    
    	result = verify_mock_cdr_record(test, &alice_expected_one, 4);
    
    	return result;
    }
    
    AST_TEST_DEFINE(test_cdr_dial_unanswered)
    {
    	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_cdr_config *, config, ao2_alloc(sizeof(*config), NULL),
    			ao2_cleanup);
    
    	struct ast_party_caller caller = ALICE_CALLERID;
    	struct ast_cdr expected = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    		.dstchannel = CHANNEL_TECH_NAME "/Bob",
    		.lastapp = "Dial",
    		.lastdata = CHANNEL_TECH_NAME "/Bob",
    		.billsec = 0,
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.disposition = AST_CDR_NOANSWER,
    		.accountcode = "100",
    		.peeraccount = "200",
    	};
    	enum ast_test_result_state result = AST_TEST_NOT_RUN;
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = __func__;
    		info->category = TEST_CATEGORY;
    		info->summary = "Test CDRs for a dial that isn't answered";
    		info->description =
    			"Test the properties of a CDR for a channel that\n"
    
    			"performs a dial operation that isn't answered";
    
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    
    	SWAP_CONFIG(config, unanswered_cdr_config);
    
    	CREATE_ALICE_CHANNEL(chan_caller, &caller, &expected);
    
    	EMULATE_APP_DATA(chan_caller, 1, "Dial", "CDRTestChannel/Bob");
    
    
    	chan_callee = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, "200", NULL, NULL, NULL, chan_caller, 0, CHANNEL_TECH_NAME "/Bob");
    
    	SET_FORMATS(chan_callee);
    
    	ast_channel_unlock(chan_callee);
    
    	ast_set_flag(ast_channel_flags(chan_callee), AST_FLAG_OUTGOING);
    	EMULATE_APP_DATA(chan_callee, 0, "AppDial", "(Outgoing Line)");
    
    	ast_channel_publish_dial(chan_caller, chan_callee, "Bob", NULL);
    	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);
    	HANGUP_CHANNEL(chan_callee, AST_CAUSE_NO_ANSWER);
    
    	result = verify_mock_cdr_record(test, &expected, 1);
    
    	return result;
    }
    
    
    AST_TEST_DEFINE(test_cdr_dial_busy)
    {
    	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_cdr_config *, config, ao2_alloc(sizeof(*config), NULL),
    			ao2_cleanup);
    
    	struct ast_party_caller caller = ALICE_CALLERID;
    	struct ast_cdr expected = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    		.dstchannel = CHANNEL_TECH_NAME "/Bob",
    		.lastapp = "Dial",
    		.lastdata = CHANNEL_TECH_NAME "/Bob",
    		.billsec = 0,
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.disposition = AST_CDR_BUSY,
    		.accountcode = "100",
    		.peeraccount = "200",
    	};
    	enum ast_test_result_state result = AST_TEST_NOT_RUN;
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = __func__;
    		info->category = TEST_CATEGORY;
    		info->summary = "Test CDRs for a dial that results in a busy";
    		info->description =
    			"Test the properties of a CDR for a channel that\n"
    
    			"performs a dial operation to an endpoint that's busy";
    
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    
    	SWAP_CONFIG(config, unanswered_cdr_config);
    
    	CREATE_ALICE_CHANNEL(chan_caller, &caller, &expected);
    
    	EMULATE_APP_DATA(chan_caller, 1, "Dial", CHANNEL_TECH_NAME "/Bob");
    
    
    	chan_callee = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, "200", NULL, NULL, NULL, chan_caller, 0, CHANNEL_TECH_NAME "/Bob");
    
    	SET_FORMATS(chan_callee);
    
    	ast_channel_unlock(chan_callee);
    
    	ast_set_flag(ast_channel_flags(chan_callee), AST_FLAG_OUTGOING);
    	EMULATE_APP_DATA(chan_callee, 0, "AppDial", "(Outgoing Line)");
    
    	ast_channel_publish_dial(chan_caller, chan_callee, "Bob", NULL);
    	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);
    	HANGUP_CHANNEL(chan_callee, AST_CAUSE_BUSY);
    
    	result = verify_mock_cdr_record(test, &expected, 1);
    
    	return result;
    }
    
    AST_TEST_DEFINE(test_cdr_dial_congestion)
    {
    	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_cdr_config *, config, ao2_alloc(sizeof(*config), NULL),
    			ao2_cleanup);
    
    	struct ast_party_caller caller = ALICE_CALLERID;
    	struct ast_cdr expected = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    		.dstchannel = CHANNEL_TECH_NAME "/Bob",
    		.lastapp = "Dial",
    		.lastdata = CHANNEL_TECH_NAME "/Bob",
    		.billsec = 0,
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.disposition = AST_CDR_CONGESTION,
    		.accountcode = "100",
    		.peeraccount = "200",
    	};
    	enum ast_test_result_state result = AST_TEST_NOT_RUN;
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = __func__;
    		info->category = TEST_CATEGORY;
    		info->summary = "Test CDRs for a dial that results in congestion";
    		info->description =
    			"Test the properties of a CDR for a channel that\n"
    
    			"performs a dial operation to an endpoint that's congested";
    
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    
    	SWAP_CONFIG(config, congestion_cdr_config);
    
    	CREATE_ALICE_CHANNEL(chan_caller, &caller, &expected);
    
    	EMULATE_APP_DATA(chan_caller, 1, "Dial", CHANNEL_TECH_NAME "/Bob");
    
    
    	chan_callee = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, "200", NULL, NULL, NULL, chan_caller, 0, CHANNEL_TECH_NAME "/Bob");
    
    	SET_FORMATS(chan_callee);
    
    	ast_channel_unlock(chan_callee);
    
    	ast_set_flag(ast_channel_flags(chan_callee), AST_FLAG_OUTGOING);
    	EMULATE_APP_DATA(chan_callee, 0, "AppDial", "(Outgoing Line)");
    
    	ast_channel_publish_dial(chan_caller, chan_callee, "Bob", NULL);
    	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);
    	HANGUP_CHANNEL(chan_callee, AST_CAUSE_CONGESTION);
    
    	result = verify_mock_cdr_record(test, &expected, 1);
    
    	return result;
    }
    
    AST_TEST_DEFINE(test_cdr_dial_unavailable)
    {
    	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_cdr_config *, config, ao2_alloc(sizeof(*config), NULL),
    			ao2_cleanup);
    
    	struct ast_party_caller caller = ALICE_CALLERID;
    	struct ast_cdr expected = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    		.dstchannel = CHANNEL_TECH_NAME "/Bob",
    		.lastapp = "Dial",
    		.lastdata = CHANNEL_TECH_NAME "/Bob",
    		.billsec = 0,
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.disposition = AST_CDR_FAILED,
    		.accountcode = "100",
    		.peeraccount = "200",
    	};
    	enum ast_test_result_state result = AST_TEST_NOT_RUN;
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = __func__;
    		info->category = TEST_CATEGORY;
    		info->summary = "Test CDRs for a dial that results in unavailable";
    		info->description =
    			"Test the properties of a CDR for a channel that\n"
    
    			"performs a dial operation to an endpoint that's unavailable";
    
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    
    	SWAP_CONFIG(config, unanswered_cdr_config);
    
    	CREATE_ALICE_CHANNEL(chan_caller, &caller, &expected);
    
    	EMULATE_APP_DATA(chan_caller, 1, "Dial", CHANNEL_TECH_NAME "/Bob");
    
    
    	chan_callee = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, "200", NULL, NULL, NULL, chan_caller, 0, CHANNEL_TECH_NAME "/Bob");
    
    	SET_FORMATS(chan_callee);
    
    	ast_channel_unlock(chan_callee);
    
    	ast_set_flag(ast_channel_flags(chan_callee), AST_FLAG_OUTGOING);
    	EMULATE_APP_DATA(chan_callee, 0, "AppDial", "(Outgoing Line)");
    
    	ast_channel_publish_dial(chan_caller, chan_callee, "Bob", NULL);
    	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);
    	HANGUP_CHANNEL(chan_callee, AST_CAUSE_NO_ROUTE_DESTINATION);
    
    	result = verify_mock_cdr_record(test, &expected, 1);
    
    	return result;
    }
    
    AST_TEST_DEFINE(test_cdr_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);
    	RAII_VAR(struct ast_cdr_config *, config, ao2_alloc(sizeof(*config), NULL),
    			ao2_cleanup);
    
    	struct ast_party_caller caller = ALICE_CALLERID;
    	struct ast_cdr expected = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    		.dstchannel = CHANNEL_TECH_NAME "/Bob",
    		.lastapp = "Dial",
    		.lastdata = CHANNEL_TECH_NAME "/Bob",
    		.billsec = 0,
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.disposition = AST_CDR_NOANSWER,
    		.accountcode = "100",
    		.peeraccount = "200",
    	};
    	enum ast_test_result_state result = AST_TEST_NOT_RUN;
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = __func__;
    		info->category = TEST_CATEGORY;
    		info->summary = "Test CDRs for a dial where the caller cancels";
    		info->description =
    			"Test the properties of a CDR for a channel that\n"
    			"performs a dial operation to an endpoint but then decides\n"
    
    			"to hang up, cancelling the dial";
    
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    
    	SWAP_CONFIG(config, unanswered_cdr_config);
    
    	CREATE_ALICE_CHANNEL(chan_caller, &caller, &expected);
    
    	EMULATE_APP_DATA(chan_caller, 1, "Dial", CHANNEL_TECH_NAME "/Bob");
    
    
    	chan_callee = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, "200", NULL, NULL, NULL, chan_caller, 0, CHANNEL_TECH_NAME "/Bob");
    
    	SET_FORMATS(chan_callee);
    
    	ast_channel_unlock(chan_callee);
    
    	ast_set_flag(ast_channel_flags(chan_callee), AST_FLAG_OUTGOING);
    	EMULATE_APP_DATA(chan_callee, 0, "AppDial", "(Outgoing Line)");
    
    	ast_channel_publish_dial(chan_caller, chan_callee, "Bob", NULL);
    	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);
    	HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL);
    
    	result = verify_mock_cdr_record(test, &expected, 1);
    
    	return result;
    }
    
    AST_TEST_DEFINE(test_cdr_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);
    	RAII_VAR(struct ast_cdr_config *, config, ao2_alloc(sizeof(*config), NULL),
    			ao2_cleanup);
    
    	struct ast_party_caller caller = ALICE_CALLERID;
    	struct ast_cdr bob_expected = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    		.dstchannel = CHANNEL_TECH_NAME "/Bob",
    		.lastapp = "Dial",
    		.lastdata = CHANNEL_TECH_NAME "/Bob&" CHANNEL_TECH_NAME "/Charlie&" CHANNEL_TECH_NAME "/David",
    		.billsec = 0,
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.disposition = AST_CDR_NOANSWER,
    		.accountcode = "100",
    		.peeraccount = "200",
    	};
    	struct ast_cdr charlie_expected = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    		.dstchannel = CHANNEL_TECH_NAME "/Charlie",
    		.lastapp = "Dial",
    		.lastdata = CHANNEL_TECH_NAME "/Bob&" CHANNEL_TECH_NAME "/Charlie&" CHANNEL_TECH_NAME "/David",
    		.billsec = 0,
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.disposition = AST_CDR_BUSY,
    		.accountcode = "100",
    		.peeraccount = "300",
    	};
    	struct ast_cdr david_expected = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    		.dstchannel = CHANNEL_TECH_NAME "/David",
    		.lastapp = "Dial",
    		.lastdata = CHANNEL_TECH_NAME "/Bob&" CHANNEL_TECH_NAME "/Charlie&" CHANNEL_TECH_NAME "/David",
    		.billsec = 0,
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.disposition = AST_CDR_CONGESTION,
    		.accountcode = "100",
    		.peeraccount = "400",
    	};
    	enum ast_test_result_state result = AST_TEST_NOT_RUN;
    
    	struct ast_cdr *expected = &bob_expected;
    	bob_expected.next = &charlie_expected;
    	charlie_expected.next = &david_expected;
    
    	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.";
    
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    
    	SWAP_CONFIG(config, congestion_cdr_config);
    
    	CREATE_ALICE_CHANNEL(chan_caller, &caller, &bob_expected);
    	COPY_IDS(chan_caller, &charlie_expected);
    	COPY_IDS(chan_caller, &david_expected);
    
    	/* Channel enters Dial app */
    	EMULATE_APP_DATA(chan_caller, 1, "Dial", CHANNEL_TECH_NAME "/Bob&" CHANNEL_TECH_NAME "/Charlie&" CHANNEL_TECH_NAME "/David");
    
    	/* Outbound channels are created */
    
    	chan_bob = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, "200", NULL, NULL, NULL, chan_caller, 0, CHANNEL_TECH_NAME "/Bob");
    
    	SET_FORMATS(chan_bob);
    
    	ast_channel_unlock(chan_bob);
    
    	ast_set_flag(ast_channel_flags(chan_bob), AST_FLAG_OUTGOING);
    	EMULATE_APP_DATA(chan_bob, 0, "AppDial", "(Outgoing Line)");
    
    
    	chan_charlie = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, "300", NULL, NULL, NULL, chan_caller, 0, CHANNEL_TECH_NAME "/Charlie");
    
    	SET_FORMATS(chan_charlie);
    
    	ast_channel_unlock(chan_charlie);
    
    	ast_set_flag(ast_channel_flags(chan_charlie), AST_FLAG_OUTGOING);
    	EMULATE_APP_DATA(chan_charlie, 0, "AppDial", "(Outgoing Line)");
    
    
    	chan_david = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, "400", NULL, NULL, NULL, chan_caller, 0, CHANNEL_TECH_NAME "/David");
    
    	SET_FORMATS(chan_charlie);
    
    	ast_channel_unlock(chan_david);
    
    	ast_set_flag(ast_channel_flags(chan_david), AST_FLAG_OUTGOING);
    	EMULATE_APP_DATA(chan_david, 0, "AppDial", "(Outgoing Line)");
    
    	/* Dial starts */
    	ast_channel_publish_dial(chan_caller, chan_bob, "Bob", NULL);
    	ast_channel_publish_dial(chan_caller, chan_charlie, "Charlie", NULL);
    	ast_channel_publish_dial(chan_caller, chan_david, "David", NULL);
    	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);
    
    	result = verify_mock_cdr_record(test, expected, 3);
    
    	return result;
    }
    
    AST_TEST_DEFINE(test_cdr_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);
    	RAII_VAR(struct ast_cdr_config *, config, ao2_alloc(sizeof(*config), NULL),
    			ao2_cleanup);
    
    	struct ast_party_caller caller = ALICE_CALLERID;
    	struct ast_cdr bob_expected_one = {
    		.clid = "\"\" <>",
    		.src = "",
    		.dst = "s",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Bob",
    		.lastapp = "Wait",
    		.lastdata = "1",
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.disposition = AST_CDR_ANSWERED,
    		.accountcode = "200",
    	};
    	struct ast_cdr alice_expected_two = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    		.lastapp = "Wait",
    		.lastdata = "1",
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.disposition = AST_CDR_ANSWERED,
    		.accountcode = "100",
    		.next = &bob_expected_one,
    	};
    	struct ast_cdr alice_expected_one = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    		.dstchannel = CHANNEL_TECH_NAME "/Bob",
    		.lastapp = "Dial",
    		.lastdata = CHANNEL_TECH_NAME "/Bob",
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.disposition = AST_CDR_ANSWERED,
    		.accountcode = "100",
    		.peeraccount = "200",
    		.next = &alice_expected_two,
    	};
    	enum ast_test_result_state result = AST_TEST_NOT_RUN;
    
    	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 CDR, it gets ended at that point, and\n"
    			"that it gets finalized appropriately. We should get three CDRs in\n"
    			"the end - one for the dial, and one for each CDR as they continued\n"
    
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    
    	SWAP_CONFIG(config, debug_cdr_config);
    
    	CREATE_ALICE_CHANNEL(chan_caller, &caller, &alice_expected_one);
    	COPY_IDS(chan_caller, &alice_expected_two);
    
    	EMULATE_APP_DATA(chan_caller, 1, "Dial", CHANNEL_TECH_NAME "/Bob");
    
    
    	chan_callee = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, "200", NULL, NULL, NULL, chan_caller, 0, CHANNEL_TECH_NAME "/Bob");
    
    	SET_FORMATS(chan_callee);
    
    	ast_channel_unlock(chan_callee);
    
    	ast_set_flag(ast_channel_flags(chan_callee), AST_FLAG_OUTGOING);
    	COPY_IDS(chan_callee, &bob_expected_one);
    
    	ast_channel_publish_dial(chan_caller, chan_callee, "Bob", NULL);
    	ast_channel_state_set(chan_caller, AST_STATE_RINGING);
    	ast_channel_publish_dial(chan_caller, chan_callee, NULL, "ANSWER");
    
    	ast_channel_state_set(chan_caller, AST_STATE_UP);
    	ast_clear_flag(ast_channel_flags(chan_callee), AST_FLAG_OUTGOING);
    	ast_channel_state_set(chan_callee, AST_STATE_UP);
    
    	EMULATE_APP_DATA(chan_caller, 2, "Wait", "1");
    	EMULATE_APP_DATA(chan_callee, 1, "Wait", "1");
    
    	HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL);
    	HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL);
    
    	result = verify_mock_cdr_record(test, &alice_expected_one, 3);
    	return result;
    }
    
    AST_TEST_DEFINE(test_cdr_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);
    
    	RAII_VAR(struct ast_cdr_config *, config, ao2_alloc(sizeof(*config), NULL),
    			ao2_cleanup);
    	struct timespec to_sleep = {1, 0};
    	enum ast_test_result_state result = AST_TEST_NOT_RUN;
    
    	struct ast_party_caller caller = ALICE_CALLERID;
    	struct ast_cdr expected = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    		.dstchannel = CHANNEL_TECH_NAME "/Bob",
    		.lastapp = "Dial",
    		.lastdata = CHANNEL_TECH_NAME "/Bob",
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.billsec = 1,
    		.disposition = AST_CDR_ANSWERED,
    		.accountcode = "100",
    		.peeraccount = "200",
    	};
    
    	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;
    	}
    
    	SWAP_CONFIG(config, debug_cdr_config);
    
    	CREATE_ALICE_CHANNEL(chan_caller, &caller, &expected);
    
    	EMULATE_APP_DATA(chan_caller, 1, "Dial", CHANNEL_TECH_NAME "/Bob");
    
    
    	chan_callee = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, "200", NULL, NULL, NULL, chan_caller, 0, CHANNEL_TECH_NAME "/Bob");
    
    	SET_FORMATS(chan_callee);
    
    	ast_channel_unlock(chan_callee);
    
    	ast_set_flag(ast_channel_flags(chan_callee), AST_FLAG_OUTGOING);
    	EMULATE_APP_DATA(chan_callee, 0, "AppDial", "(Outgoing Line)");
    
    	ast_channel_publish_dial(chan_caller, chan_callee, "Bob", NULL);
    	ast_channel_state_set(chan_caller, AST_STATE_RINGING);
    	ast_channel_publish_dial(chan_caller, chan_callee, NULL, "ANSWER");
    
    	ast_channel_state_set(chan_caller, AST_STATE_UP);
    	ast_channel_state_set(chan_callee, AST_STATE_UP);
    
    	bridge = ast_bridge_basic_new();
    	ast_test_validate(test, bridge != NULL);
    
    	ast_test_validate(test, !ast_bridge_impart(bridge, chan_caller, NULL, NULL, AST_BRIDGE_IMPART_CHAN_DEPARTABLE));
    	ast_test_validate(test, !ast_bridge_impart(bridge, chan_callee, NULL, NULL, AST_BRIDGE_IMPART_CHAN_DEPARTABLE));
    
    
    	ast_bridge_depart(chan_caller);
    	ast_bridge_depart(chan_callee);
    
    	HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL);
    	HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL);
    
    	result = verify_mock_cdr_record(test, &expected, 1);
    	return result;
    }
    
    AST_TEST_DEFINE(test_cdr_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);
    
    	RAII_VAR(struct ast_cdr_config *, config, ao2_alloc(sizeof(*config), NULL),
    			ao2_cleanup);
    	struct timespec to_sleep = {1, 0};
    	enum ast_test_result_state result = AST_TEST_NOT_RUN;
    
    	struct ast_party_caller caller = ALICE_CALLERID;
    	struct ast_cdr expected = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    		.dstchannel = CHANNEL_TECH_NAME "/Bob",
    		.lastapp = "Dial",
    		.lastdata = CHANNEL_TECH_NAME "/Bob",
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.billsec = 1,
    		.disposition = AST_CDR_ANSWERED,
    		.accountcode = "100",
    		.peeraccount = "200",
    	};
    
    	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;
    	}
    
    	SWAP_CONFIG(config, debug_cdr_config);
    
    	CREATE_ALICE_CHANNEL(chan_caller, &caller, &expected);
    
    	EMULATE_APP_DATA(chan_caller, 1, "Dial", CHANNEL_TECH_NAME "/Bob");
    
    
    	chan_callee = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, "200", NULL, NULL, NULL, chan_caller, 0, CHANNEL_TECH_NAME "/Bob");
    
    	SET_FORMATS(chan_callee);
    
    	ast_channel_unlock(chan_callee);
    
    	ast_set_flag(ast_channel_flags(chan_callee), AST_FLAG_OUTGOING);
    	EMULATE_APP_DATA(chan_callee, 0, "AppDial", "(Outgoing Line)");
    
    	ast_channel_publish_dial(chan_caller, chan_callee, "Bob", NULL);
    	ast_channel_state_set(chan_caller, AST_STATE_RINGING);
    	ast_channel_publish_dial(chan_caller, chan_callee, NULL, "ANSWER");
    
    	ast_channel_state_set(chan_caller, AST_STATE_UP);
    	ast_channel_state_set(chan_callee, AST_STATE_UP);
    
    	bridge = ast_bridge_basic_new();
    	ast_test_validate(test, bridge != NULL);
    
    	ast_test_validate(test, !ast_bridge_impart(bridge, chan_callee, NULL, NULL, AST_BRIDGE_IMPART_CHAN_DEPARTABLE));
    
    	ast_test_validate(test, !ast_bridge_impart(bridge, chan_caller, NULL, NULL, AST_BRIDGE_IMPART_CHAN_DEPARTABLE));
    
    	ast_bridge_depart(chan_caller);
    	ast_bridge_depart(chan_callee);
    
    	HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL);
    	HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL);
    
    	result = verify_mock_cdr_record(test, &expected, 1);
    	return result;
    }
    
    AST_TEST_DEFINE(test_cdr_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);
    
    	RAII_VAR(struct ast_cdr_config *, config, ao2_alloc(sizeof(*config), NULL),
    			ao2_cleanup);
    	struct timespec to_sleep = {1, 0};
    	enum ast_test_result_state result = AST_TEST_NOT_RUN;
    
    	struct ast_party_caller alice_caller = ALICE_CALLERID;
    	struct ast_party_caller charlie_caller = CHARLIE_CALLERID;
    	struct ast_cdr charlie_expected_two = {
    		.clid = "\"Charlie\" <300>",
    		.src = "300",
    		.dst = "300",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Charlie",
    		.dstchannel = CHANNEL_TECH_NAME "/Bob",
    		.lastapp = "Dial",
    		.lastdata = CHANNEL_TECH_NAME "/David",
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.billsec = 1,
    		.disposition = AST_CDR_ANSWERED,
    		.accountcode = "300",
    		.peeraccount = "200",
    	};
    	struct ast_cdr charlie_expected_one = {
    		.clid = "\"Charlie\" <300>",
    		.src = "300",
    		.dst = "300",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Charlie",
    		.dstchannel = CHANNEL_TECH_NAME "/David",
    		.lastapp = "Dial",
    		.lastdata = CHANNEL_TECH_NAME "/David",
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.billsec = 1,
    		.disposition = AST_CDR_ANSWERED,
    		.accountcode = "300",
    		.peeraccount = "400",
    		.next = &charlie_expected_two,
    	};
    
    	struct ast_cdr bob_expected_one = {
    		.clid = "\"Bob\" <200>",
    		.src = "200",
    		.dst = "200",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Bob",
    		.dstchannel = CHANNEL_TECH_NAME "/David",
    		.lastapp = "AppDial",
    		.lastdata = "(Outgoing Line)",
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.billsec = 1,
    		.disposition = AST_CDR_ANSWERED,
    		.accountcode = "200",
    		.peeraccount = "400",
    		.next = &charlie_expected_one,
    	};
    
    	struct ast_cdr alice_expected_three = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    
    		.dstchannel = CHANNEL_TECH_NAME "/David",
    
    		.lastapp = "Dial",
    		.lastdata = CHANNEL_TECH_NAME "/Bob",
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.billsec = 1,
    		.disposition = AST_CDR_ANSWERED,
    		.accountcode = "100",
    
    		.peeraccount = "400",
    		.next = &bob_expected_one,
    
    	};
    	struct ast_cdr alice_expected_two = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    
    		.dstchannel = CHANNEL_TECH_NAME "/Charlie",
    
    		.lastapp = "Dial",
    		.lastdata = CHANNEL_TECH_NAME "/Bob",
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.billsec = 1,
    		.disposition = AST_CDR_ANSWERED,
    		.accountcode = "100",
    
    		.peeraccount = "300",
    
    		.next = &alice_expected_three,
    	};
    	struct ast_cdr alice_expected_one = {
    		.clid = "\"Alice\" <100>",
    		.src = "100",
    		.dst = "100",
    		.dcontext = "default",
    		.channel = CHANNEL_TECH_NAME "/Alice",
    		.dstchannel = CHANNEL_TECH_NAME "/Bob",
    		.lastapp = "Dial",
    		.lastdata = CHANNEL_TECH_NAME "/Bob",
    		.amaflags = AST_AMA_DOCUMENTATION,
    		.billsec = 1,
    		.disposition = AST_CDR_ANSWERED,
    		.accountcode = "100",
    		.peeraccount = "200",
    		.next = &alice_expected_two,
    	};
    
    	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;
    	}
    
    	SWAP_CONFIG(config, debug_cdr_config);
    
    	CREATE_ALICE_CHANNEL(chan_alice, &alice_caller, &alice_expected_one);
    	COPY_IDS(chan_alice, &alice_expected_two);
    	COPY_IDS(chan_alice, &alice_expected_three);
    
    	EMULATE_APP_DATA(chan_alice, 1, "Dial", CHANNEL_TECH_NAME "/Bob");
    
    
    	chan_bob = ast_channel_alloc(0, AST_STATE_DOWN, "200", "Bob", "200", "200", "default", NULL, NULL, 0, CHANNEL_TECH_NAME "/Bob");
    
    	SET_FORMATS(chan_bob);
    
    	ast_channel_unlock(chan_bob);
    
    	ast_set_flag(ast_channel_flags(chan_bob), AST_FLAG_OUTGOING);
    	EMULATE_APP_DATA(chan_bob, 0, "AppDial", "(Outgoing Line)");
    
    	ast_copy_string(bob_expected_one.uniqueid, ast_channel_uniqueid(chan_bob), sizeof(bob_expected_one.uniqueid));
    	ast_copy_string(bob_expected_one.linkedid, ast_channel_linkedid(chan_alice), sizeof(bob_expected_one.linkedid));
    
    
    	CREATE_CHARLIE_CHANNEL(chan_charlie, &charlie_caller, &charlie_expected_one);
    	EMULATE_APP_DATA(chan_charlie, 1, "Dial", CHANNEL_TECH_NAME "/David");
    	ast_copy_string(charlie_expected_one.uniqueid, ast_channel_uniqueid(chan_charlie), sizeof(charlie_expected_one.uniqueid));
    	ast_copy_string(charlie_expected_one.linkedid, ast_channel_linkedid(chan_alice), sizeof(charlie_expected_one.linkedid));
    	ast_copy_string(charlie_expected_two.uniqueid, ast_channel_uniqueid(chan_charlie), sizeof(charlie_expected_two.uniqueid));
    	ast_copy_string(charlie_expected_two.linkedid, ast_channel_linkedid(chan_alice), sizeof(charlie_expected_two.linkedid));
    
    
    	chan_david = ast_channel_alloc(0, AST_STATE_DOWN, "400", "David", "400", "400", "default", NULL, NULL, 0, CHANNEL_TECH_NAME "/David");
    
    	SET_FORMATS(chan_david);
    
    	ast_channel_unlock(chan_david);
    
    	ast_set_flag(ast_channel_flags(chan_david), AST_FLAG_OUTGOING);
    	EMULATE_APP_DATA(chan_david, 0, "AppDial", "(Outgoing Line)");
    
    	ast_channel_publish_dial(chan_alice, chan_bob, "Bob", NULL);
    	ast_channel_state_set(chan_alice, AST_STATE_RINGING);
    	ast_channel_publish_dial(chan_charlie, chan_david, "David", NULL);
    	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");
    
    	ast_channel_state_set(chan_alice, AST_STATE_UP);
    	ast_channel_state_set(chan_bob, AST_STATE_UP);
    	ast_channel_state_set(chan_charlie, AST_STATE_UP);