diff options
Diffstat (limited to 'asterisk/Asterisk_Tests.ttcn')
-rw-r--r-- | asterisk/Asterisk_Tests.ttcn | 1286 |
1 files changed, 1286 insertions, 0 deletions
diff --git a/asterisk/Asterisk_Tests.ttcn b/asterisk/Asterisk_Tests.ttcn new file mode 100644 index 00000000..dccf9fa2 --- /dev/null +++ b/asterisk/Asterisk_Tests.ttcn @@ -0,0 +1,1286 @@ +module Asterisk_Tests { + +/* Asterisk test suite in TTCN-3 + * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All rights reserved. + * Author: Pau Espin Pedrol <pespin@sysmocom.de> + * + * Released under the terms of GNU General Public License, Version 2 or + * (at your option) any later version. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +import from TCCOpenSecurity_Functions all; +import from General_Types all; +import from Osmocom_Types all; +import from Native_Functions all; +import from Misc_Helpers all; +import from TELNETasp_PortType all; +import from AMI_Functions all; + +import from SDP_Types all; +import from SDP_Templates all; + +import from SIP_Emulation all; +import from SIPmsg_Types all; +import from SIP_Templates all; + +import from SIP_ConnectionHandler all; +import from IMS_ConnectionHandler all; + +modulepar { + charstring mp_local_sip_host := "127.0.0.2"; + integer mp_local_sip_port := 5060; + charstring mp_remote_sip_host := "127.0.0.1"; + integer mp_remote_sip_port := 5060; + + charstring mp_local_ims_host := "127.0.0.3"; + integer mp_local_ims_port := 5060; + charstring mp_ims_domain := "ims.mnc001.mcc238.3gppnetwork.org" + charstring mp_ims_imsi := "238010000090828"; + charstring mp_ims_imei := "35876110-027790-0"; + + /* Asterisk AMI: */ + charstring mp_ami_remote_host := "127.0.0.1"; + integer mp_ami_remote_port := 5038; + charstring mp_ami_local_host := "0.0.0.0"; + integer mp_ami_local_port := 0; + charstring mp_ami_user := "test_user"; + charstring mp_ami_secret := "1234"; + charstring mp_volte_ims_outbound_registration := "volte_ims"; + /* Current default by pjproject (timeout_timer_val) is set to 32s, and not changed by Asterisk */ + integer mp_volte_ims_outbound_register_timeout := 32; + /* It can sometimes take up to ~30s for Asterisk to finish startup and sending the FullyBooted event... */ + float mp_ami_ev_fullybooted_timeout := 30.0; +} + +type component test_CT { + /* Manages all local VoIP users Asterisk is serving: */ + var SIP_Emulation_CT vc_SIP; + /* Manages the IMS server Asterisk connects to: */ + var SIP_Emulation_CT vc_IMS; + + /* Connection towards Asterisk AMI iface: */ + var AMI_Adapter_CT vc_AMI; + port AMI_Msg_PT AMI_CLIENT; + + port Coord_PT COORD; + port IMSCoord_PT IMS_COORD; + + /* Store Channel name obtained from Newchannel AMI event: */ + var charstring g_asterisk_chan_name; +} + +const charstring broadcast_sip_extension := "0500"; + + +function f_init_ConnHdlrPars(integer idx := 1) runs on test_CT return SIPConnHdlrPars { + var template (value) CallPars cp := t_CallPars(mp_local_sip_host, 1234 + 2*idx); + var template (value) SIPConnHdlrPars pars := t_Pars(mp_local_sip_host, + mp_local_sip_port, + mp_remote_sip_host, + mp_remote_sip_port, + "0" & int2str(str2int(broadcast_sip_extension) + idx), + cp := cp); + return valueof(pars); +} + +function f_init_IMS_ConnHdlrPars(integer idx := 1) runs on test_CT return IMS_ConnHdlrPars { + var template (value) IMS_CallPars cp := t_IMS_CallPars(mp_local_ims_host, 1234 + 2*idx); + var template (value) IMS_ConnHdlrPars pars := t_IMS_Pars(mp_local_ims_host, + mp_local_ims_port, + mp_ims_domain, + mp_ims_imsi, + mp_ims_imei, + cp := cp); + return valueof(pars); +} + +/* Initialize connection towards Asterisk AMI */ +private function f_init_ami() runs on test_CT { + var charstring id := "Asterisk_Tests_AMI_EMU"; + vc_AMI := AMI_Adapter_CT.create(id) alive; + connect(self:AMI_CLIENT, vc_AMI:CLIENT); + + var AMI_Adapter_Parameters ami_pars := { + remote_host := mp_ami_remote_host, + remote_port := mp_ami_remote_port, + local_host := mp_ami_local_host, + local_port := mp_ami_local_port, + welcome_str := c_default_AMI_Adapter_pars.welcome_str + }; + vc_AMI.start(f_AMI_Adapter_main(ami_pars)); + + f_ami_action_login(AMI_CLIENT, mp_ami_user, mp_ami_secret); + + timer tReady; + tReady.start(mp_ami_ev_fullybooted_timeout); + alt { + [] AMI_CLIENT.receive(tr_AMI_Event_FullyBooted); + [] as_ami_rx_ignore(AMI_CLIENT); + [] tReady.timeout { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("AMI FullyBooted timeout: ")); + } + } +} + +/* Local SIP UAs */ +private function f_init_sip_local() runs on test_CT { + var charstring id := "Asterisk_Tests_LOCAL_SIP_EMU"; + f_init_sip(vc_SIP, id); +} + +/* IMS Server connection */ +private function f_init_sip_ims() runs on test_CT { + var charstring id := "Asterisk_Tests_IMS_SIP_EMU"; + f_init_sip(vc_IMS, id); +} + +function f_init() runs on test_CT { + var charstring id; + + f_init_ami(); + f_init_sip_local(); + f_init_sip_ims(); + log("end of f_init"); +} + +function f_shutdown() runs on test_CT { + /* Tear down AMI Adapter to avoid it keep receiving data from Asterisk + * and sending it to us after we stopped, causing error (Broken Pipe): */ + vc_AMI.stop; + vc_AMI.done; + log("end of ", testcasename()); + setverdict(pass); +} + +function f_start_handler(void_fn fn, SIPConnHdlrPars pars) +runs on test_CT return SIPConnHdlr { + var SIPConnHdlr vc_conn; + var charstring id := testcasename() & "-ConnHdlr-" & pars.user; + + vc_conn := SIPConnHdlr.create(id) alive; + + connect(vc_conn:SIP, vc_SIP:CLIENT); + connect(vc_conn:SIP_PROC, vc_SIP:CLIENT_PROC); + + connect(vc_conn:COORD, self:COORD); + + vc_conn.start(f_handler_init(fn, id, pars)); + return vc_conn; +} + +function f_start_handler_IMS(ims_void_fn fn, IMS_ConnHdlrPars pars) +runs on test_CT return IMS_ConnHdlr { + var IMS_ConnHdlr vc_conn; + var charstring id := testcasename() & "-IMS_ConnHdlr-" & pars.subscr.imsi; + + vc_conn := IMS_ConnHdlr.create(id) alive; + + connect(vc_conn:SIP, vc_IMS:CLIENT); + connect(vc_conn:SIP_PROC, vc_IMS:CLIENT_PROC); + + connect(vc_conn:COORD, self:IMS_COORD); + + vc_conn.start(f_ims_handler_init(fn, id, pars)); + return vc_conn; +} + +/* Test SIP registration of local clients */ +private function f_TC_internal_registration(charstring id) runs on SIPConnHdlr { + + f_SIP_register(); + /* Trigger unregistration: */ + f_sleep(1.0); + f_SIP_unregister(); + setverdict(pass); +} +testcase TC_internal_registration() runs on test_CT { + var SIPConnHdlrPars pars; + var SIPConnHdlr vc_conn; + f_init(); + pars := f_init_ConnHdlrPars(); + vc_conn := f_start_handler(refers(f_TC_internal_registration), pars); + vc_conn.done; + f_shutdown(); +} + +/* Successful SIP MO-MT Call between local clients: */ +private function f_TC_internal_register_establish_call_mo() runs on SIPConnHdlr { + f_SIP_register(); + COORD.send(COORD_CMD_REGISTERED); + + COORD.receive(COORD_CMD_START); + f_SIP_mo_call_setup(exp_update_to_direct_rtp := g_pars.cp.exp_update_to_direct_rtp); + COORD.send(COORD_CMD_CALL_ESTABLISHED); +} +private function f_TC_internal_hangup_call_mo_unregister() runs on SIPConnHdlr { + COORD.receive(COORD_CMD_HANGUP); + f_SIP_do_call_hangup(); + + COORD.receive(COORD_CMD_UNREGISTER); + f_SIP_unregister(); +} +private function f_TC_internal_call_mo(charstring id) runs on SIPConnHdlr { + f_TC_internal_register_establish_call_mo(); + f_TC_internal_hangup_call_mo_unregister(); + setverdict(pass); +} +private function f_TC_internal_call_mo_with_holdresume(charstring id) runs on SIPConnHdlr { + f_TC_internal_register_establish_call_mo(); + f_sleep(1.0); + f_SIP_do_call_hold(); + f_sleep(1.0); + f_SIP_do_call_resume(); + f_TC_internal_hangup_call_mo_unregister(); + setverdict(pass); +} +private function f_TC_internal_call_mo_with_holdswitchresume(charstring id) runs on SIPConnHdlr { + var CallPars cp0; + f_TC_internal_register_establish_call_mo(); + f_sleep(1.0); + f_SIP_do_call_hold(); + + /* Backup state of 1st call params: */ + cp0 := g_pars.cp; + g_pars.cp.called := valueof(ts_SipAddr(ts_HostPort(g_pars.remote_sip_host), + ts_UserInfo("123456"))); + COORD.receive(COORD_CMD_START); + f_SIP_mo_call_setup(exp_update_to_direct_rtp := false); + log ("SIP: 2nd call ongoing"); + COORD.send(COORD_CMD_CALL_ESTABLISHED); + COORD.receive(COORD_CMD_HANGUP); + f_SIP_do_call_hangup(); + + /* Restore 1s call metadata before hanging it up here: */ + g_pars.cp := cp0; + f_SIP_do_call_resume(); + f_TC_internal_hangup_call_mo_unregister(); + setverdict(pass); +} +private function f_TC_internal_call_mo_rejected(charstring id) runs on SIPConnHdlr { + f_SIP_register(); + COORD.send(COORD_CMD_REGISTERED); + + COORD.receive(COORD_CMD_START); + f_SIP_mo_call_setup(503, "Service Unavailable"); + COORD.send(COORD_CMD_CALL_FINISHED); + + COORD.receive(COORD_CMD_UNREGISTER); + f_SIP_unregister(); + setverdict(pass); +} + +private function f_TC_internal_call_mt(charstring id) runs on SIPConnHdlr { + + f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.cp.called.addr))); + + f_SIP_register(); + COORD.send(COORD_CMD_REGISTERED); + + if (g_pars.cp.mt.exp_cancel) { + as_SIP_mt_call_cancelled(); + COORD.send(COORD_CMD_CALL_CANCELLED); + COORD.receive(COORD_CMD_UNREGISTER); + f_SIP_unregister(); + setverdict(pass); + return; + } + + as_SIP_mt_call_accept(exp_update_to_direct_rtp := g_pars.cp.exp_update_to_direct_rtp); + COORD.send(COORD_CMD_CALL_ESTABLISHED); + + if (g_pars.cp.exp_update_to_direct_rtp) { + /* Once MO hangs up, Asterisk updates us to point RTP to it: */ + as_SIP_exp_mt_call_update(g_pars.cp.sip_seq_nr + 1); + } + as_SIP_exp_call_hangup(g_pars.cp.sip_seq_nr + 1); + + COORD.send(COORD_CMD_CALL_FINISHED); + COORD.receive(COORD_CMD_UNREGISTER); + f_SIP_unregister(); + + setverdict(pass); +} +testcase TC_internal_call_momt() runs on test_CT { + var SIPConnHdlrPars pars[2]; + var SIPConnHdlr vc_conn[2]; + + f_init(); + + pars[0] := f_init_ConnHdlrPars(idx := 1); + pars[1] := f_init_ConnHdlrPars(idx := 2); + + pars[0].cp.calling := pars[0].registrar_sip_record; + pars[0].cp.called := pars[1].registrar_sip_record; + + pars[1].cp.calling := pars[0].registrar_sip_record; + pars[1].cp.called := pars[1].local_sip_record; + + vc_conn[0] := f_start_handler(refers(f_TC_internal_call_mo), pars[0]); + vc_conn[1] := f_start_handler(refers(f_TC_internal_call_mt), pars[1]); + + interleave { + [] COORD.receive(COORD_CMD_REGISTERED) from vc_conn[0]; + [] COORD.receive(COORD_CMD_REGISTERED) from vc_conn[1]; + } + + COORD.send(COORD_CMD_START) to vc_conn[0]; + + interleave { + [] COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn[0]; + [] COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn[1]; + } + + /* Call on-going */ + f_sleep(1.0); + + COORD.send(COORD_CMD_HANGUP) to vc_conn[0]; + COORD.receive(COORD_CMD_CALL_FINISHED) from vc_conn[1]; + + COORD.send(COORD_CMD_UNREGISTER) to vc_conn[0]; + COORD.send(COORD_CMD_UNREGISTER) to vc_conn[1]; + + vc_conn[0].done; + vc_conn[1].done; + f_shutdown(); +} + +/* One of the users calls (INVITE) shared extension, which makes all other user + * equipments ring (INVITE). The first one to pick up the call (OK 200) gets the + * call established (ACK), others get a CANCEL event. */ +private function TC_internal_call_all_Nregistered(integer num_conns := 2) runs on test_CT { + var SIPConnHdlrList vc_conn_list := {}; + const integer vc_conn_mo_idx := 0; /* Index of MO leg in vc_conn_list */ + const integer vc_conn_mt_idx := 1; /* Index of MT leg in vc_conn_list, peer picking up first the call */ + var SipAddr broadcast_sip_record; + var SIPConnHdlrPars pars_mo; + + f_init(); + + broadcast_sip_record := valueof(ts_SipAddr(ts_HostPort(mp_local_sip_host), + ts_UserInfo(broadcast_sip_extension))); + + for (var integer i := 0; i < num_conns; i := i + 1) { + var SIPConnHdlrPars pars; + var SIPConnHdlr vc_conn; + pars := f_init_ConnHdlrPars(idx := i + 1); + if (i == vc_conn_mo_idx) { /* MO */ + pars.cp.calling := pars.registrar_sip_record; + pars.cp.called := broadcast_sip_record; + vc_conn := f_start_handler(refers(f_TC_internal_call_mo), pars); + pars_mo := pars; + } else { /* MT */ + pars.cp.calling := pars_mo.registrar_sip_record; + pars.cp.called := pars.local_sip_record; + pars.cp.mt.wait_coord_cmd_pickup := true; + if (i != vc_conn_mt_idx) { + /* Only first MT picking up (OK 200 INVITE) will be ACKed, others CANCELed: */ + pars.cp.mt.exp_cancel := true; + } + vc_conn := f_start_handler(refers(f_TC_internal_call_mt), pars); + } + vc_conn_list := vc_conn_list & { vc_conn }; + } + + /* Wait all users are registered: */ + for (var integer i := 0; i < num_conns; i := i + 1) { + /* Note: "from vc_conn_list[i]" can't be used since they may arrive from components in any order: */ + COORD.receive(COORD_CMD_REGISTERED); + } + + /* Ask MO user to start the call: */ + COORD.send(COORD_CMD_START) to vc_conn_list[vc_conn_mo_idx]; + + /* Make sure the desired MT is the one picking up first the call: */ + COORD.send(COORD_CMD_PICKUP) to vc_conn_list[vc_conn_mt_idx]; + for (var integer i := 0; i < num_conns; i := i + 1) { + if (i != vc_conn_mo_idx and i != vc_conn_mt_idx) { + COORD.send(COORD_CMD_PICKUP) to vc_conn_list[i]; + } + } + + /* Pick up from other phone calls and expect CANCEL: */ + var boolean wait_mo := true, wait_mt := true; + for (var integer i := 0; i < num_conns; i := i + 1) { + alt { + [wait_mo] COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn_list[vc_conn_mo_idx] { wait_mo := false }; + [wait_mt] COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn_list[vc_conn_mt_idx] { wait_mt := false }; + [] COORD.receive(COORD_CMD_CALL_CANCELLED); + } + } + + /* Call on-going */ + f_sleep(1.0); + + COORD.send(COORD_CMD_HANGUP) to vc_conn_list[vc_conn_mo_idx]; + + COORD.receive(COORD_CMD_CALL_FINISHED) from vc_conn_list[vc_conn_mt_idx]; + + for (var integer i := 0; i < num_conns; i := i + 1) { + COORD.send(COORD_CMD_UNREGISTER) to vc_conn_list[i]; + } + + for (var integer i := 0; i < num_conns; i := i + 1) { + vc_conn_list[i].done; + } + f_shutdown(); +} +testcase TC_internal_call_all_2registered() runs on test_CT { + TC_internal_call_all_Nregistered(2); +} +testcase TC_internal_call_all_3registered() runs on test_CT { + TC_internal_call_all_Nregistered(3); +} +testcase TC_internal_call_all_4registered() runs on test_CT { + TC_internal_call_all_Nregistered(4); +} + +testcase TC_selftest() runs on test_CT { + f_ami_selftest(); + f_sip_digest_selftest(); + setverdict(pass); +} + +private function f_AMI_IMS_start_register(IMS_ConnHdlrPars pars) runs on test_CT +{ + /* Give some time for IMS_ConnHdlr to register SIP expect. This could be done through IMS_COORD. */ + f_sleep(1.0); + /* Clear events: */ + AMI_CLIENT.clear; + + /* Announce network information, this should usually happen when UE + * becomes attached to network and before IMS APN is set up: */ + f_ami_action_PJSIPAccessNetworkInfo(AMI_CLIENT, mp_volte_ims_outbound_registration, + f_ami_gen_PJSIPAccessNetworkInfo_Info_EUTRAN(pars.subscr.uli_str)); + + /* Trigger registration: */ + f_ami_action_PJSIPRegister(AMI_CLIENT, mp_volte_ims_outbound_registration); +} + +private altstep as_AMI_IMS_register_Auth(IMS_ConnHdlrPars pars, + boolean resync := false, + boolean exp_ami_ev_registered := true) runs on test_CT +{ + var charstring rand_str := oct2str(pars.subscr.auth.rand); + var charstring autn_str := oct2str(pars.subscr.auth.autn); + [not resync] AMI_CLIENT.receive(tr_AMI_Event_AuthRequest(mp_volte_ims_outbound_registration, + rand := pattern @nocase rand_str, + autn := pattern @nocase autn_str)) { + f_ami_action_AuthResponse_RES(AMI_CLIENT, + mp_volte_ims_outbound_registration, + f_str_tolower(oct2str(pars.subscr.auth.res)), + f_str_tolower(oct2str(pars.subscr.auth.ck)), + f_str_tolower(oct2str(pars.subscr.auth.ik))); + + if (exp_ami_ev_registered) { + var AMI_Msg ami_msg_rx; + var template (present) AMI_Msg ami_msg_exp := + tr_AMI_Event_Registry(f_sip_SipAddr_to_str(pars.subscr.local_sip_record), + "sip:" & mp_ims_domain, + "Registered"); + alt { + [] AMI_CLIENT.receive(ami_msg_exp); + [] AMI_CLIENT.receive(AMI_Msg:?) -> value ami_msg_rx { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("Unexpected AMI msg: ", ami_msg_rx, " vs exp: ", ami_msg_exp)); + } + } + } + } + [resync] AMI_CLIENT.receive(tr_AMI_Event_AuthRequest(mp_volte_ims_outbound_registration, + rand := pattern @nocase rand_str, + autn := pattern @nocase autn_str)) { + f_ami_action_AuthResponse_AUTS(AMI_CLIENT, + mp_volte_ims_outbound_registration, + f_str_tolower(oct2str(pars.subscr.auth.auts))); + } +} + +private function f_AMI_IMS_register(IMS_ConnHdlrPars pars, boolean resync := false) runs on test_CT +{ + f_AMI_IMS_start_register(pars); + as_AMI_IMS_register_Auth(pars, resync := resync); +} + +private function f_AMI_IMS_unregister(IMS_ConnHdlrPars pars) runs on test_CT +{ + f_ami_action_PJSIPUnregister(AMI_CLIENT, mp_volte_ims_outbound_registration, fail_others := false); + var template (present) AMI_Msg msg_expect := + tr_AMI_Event_Registry(f_sip_SipAddr_to_str(pars.subscr.local_sip_record), + "sip:" & mp_ims_domain, + "Unregistered"); + f_ami_wait_rx_msg(AMI_CLIENT, msg_expect, fail_others := false); +} + +/* Test IMS registration of VoLTE UE */ +private function f_TC_ims_registration(charstring id) runs on IMS_ConnHdlr { + f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.registrar_sip_record.addr))); + as_IMS_register(); + as_IMS_unregister(); + setverdict(pass); +} +testcase TC_ims_registration() runs on test_CT { + var IMS_ConnHdlrPars pars; + var IMS_ConnHdlr vc_conn; + f_init(); + pars := f_init_IMS_ConnHdlrPars(); + vc_conn := f_start_handler_IMS(refers(f_TC_ims_registration), pars); + + f_AMI_IMS_register(pars); + + /* Stay registered for one second */ + f_sleep(1.0); + + /* Trigger unregistration: */ + f_AMI_IMS_unregister(pars); + + vc_conn.done; + f_shutdown(); +} + +/* Test IMS registration of VoLTE UE. ISIM Resync case. */ +private function f_TC_ims_registration_resync(charstring id) runs on IMS_ConnHdlr { + f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.registrar_sip_record.addr))); + as_IMS_register(exp_auth_resync := true); + as_IMS_unregister(); + setverdict(pass); +} +testcase TC_ims_registration_resync() runs on test_CT { + var IMS_ConnHdlrPars pars; + var IMS_ConnHdlr vc_conn; + f_init(); + pars := f_init_IMS_ConnHdlrPars(); + vc_conn := f_start_handler_IMS(refers(f_TC_ims_registration_resync), pars); + + /* Emulate first Auth sync failure: */ + f_AMI_IMS_register(pars, resync := true); + /* Second auth goes well: */ + as_AMI_IMS_register_Auth(pars, resync := false); + /* Stay registered for one second */ + f_sleep(1.0); + + /* Trigger unregistration: */ + f_AMI_IMS_unregister(pars); + + vc_conn.done; + f_shutdown(); +} + +private function f_TC_ims_registration_timeout_IMS_ConnHdlr(IMS_register_early_return early_ret, boolean exp_auth_resync := false) runs on IMS_ConnHdlr { + f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.registrar_sip_record.addr))); + + as_IMS_register(exp_auth_resync := exp_auth_resync, early_ret := early_ret); + + timer Tout; + /* 1.0: Give some margin. */ + Tout.start(int2float(mp_volte_ims_outbound_register_timeout) + 1.0); + /* Make sure no new REGISTER is attempted unless directed by AMI: */ + alt { + [] as_SIP_fail_req("nothing"); + [] as_SIP_fail_resp("nothing"); + [] Tout.timeout { /* Done */ } + } + setverdict(pass); +} +/* Test initial REGISTER against IMS core timing out. */ +private function f_TC_ims_registration_timeout_initial_100Trying(charstring id) runs on IMS_ConnHdlr { + f_TC_ims_registration_timeout_IMS_ConnHdlr(IMS_REG_EARLY_RET_BEFORE_Initial_100Trying); +} +private function f_TC_ims_registration_timeout_initial_401Unauthorized(charstring id) runs on IMS_ConnHdlr { + f_TC_ims_registration_timeout_IMS_ConnHdlr(IMS_REG_EARLY_RET_BEFORE_Initial_401Unauthorized); +} +private function f_TC_ims_registration_timeout_resync_401Unauthorized(charstring id) runs on IMS_ConnHdlr { + f_TC_ims_registration_timeout_IMS_ConnHdlr(IMS_REG_EARLY_RET_BEFORE_Resync_401Unauthorized, exp_auth_resync := true); +} +private function f_TC_ims_registration_timeout_protected_100Trying(charstring id) runs on IMS_ConnHdlr { + f_TC_ims_registration_timeout_IMS_ConnHdlr(IMS_REG_EARLY_RET_BEFORE_Protected_100Trying); +} +private function f_TC_ims_registration_timeout_protected_200OK(charstring id) runs on IMS_ConnHdlr { + f_TC_ims_registration_timeout_IMS_ConnHdlr(IMS_REG_EARLY_RET_BEFORE_Protected_200OK); +} +private function f_TC_ims_registration_timeout_initial(ims_void_fn fn, + boolean answer_register := false, + boolean resync := false) runs on test_CT { + var IMS_ConnHdlrPars pars; + var IMS_ConnHdlr vc_conn; + f_init(); + pars := f_init_IMS_ConnHdlrPars(); + vc_conn := f_start_handler_IMS(fn, pars); + + f_AMI_IMS_start_register(pars); + + if (answer_register) { + as_AMI_IMS_register_Auth(pars, resync := resync, exp_ami_ev_registered := false); + } + + AMI_CLIENT.receive(tr_AMI_Event_Registry(f_sip_SipAddr_to_str(pars.subscr.local_sip_record), + "sip:" & mp_ims_domain, + "Rejected")); + + vc_conn.done; + f_shutdown(); +} +testcase TC_ims_registration_timeout_initial_100Trying() runs on test_CT { + f_TC_ims_registration_timeout_initial(refers(f_TC_ims_registration_timeout_initial_100Trying)); +} +testcase TC_ims_registration_timeout_initial_401Unauthorized() runs on test_CT { + f_TC_ims_registration_timeout_initial(refers(f_TC_ims_registration_timeout_initial_401Unauthorized)); +} +testcase TC_ims_registration_timeout_resync_401Unauthorized() runs on test_CT { + f_TC_ims_registration_timeout_initial(refers(f_TC_ims_registration_timeout_resync_401Unauthorized), + true, true); +} +testcase TC_ims_registration_timeout_protected_100Trying() runs on test_CT { + f_TC_ims_registration_timeout_initial(refers(f_TC_ims_registration_timeout_protected_100Trying), + true, false); +} +testcase TC_ims_registration_timeout_protected_200OK() runs on test_CT { + f_TC_ims_registration_timeout_initial(refers(f_TC_ims_registration_timeout_protected_200OK), + true, false); +} + +/* Test IMS re-registration based on Expires (TS 24.229 5.1.1.4.1, RFC 3261 10.3)*/ +private function f_TC_ims_registration_423_interval_too_brief(charstring id) runs on IMS_ConnHdlr { + var template (value) To to_addr; + + f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.registrar_sip_record.addr))); + + var template (present) PDU_SIP_Request exp_req := + tr_SIP_REGISTER(g_pars.registrar_sip_req_uri, + ?, + tr_From(), + tr_To(), + tr_Via_from(?), + expires := tr_Expires(int2str(g_pars.subscr.registrar_expires))); + SIP.receive(exp_req) -> value g_rx_sip_req; + + to_addr := g_rx_sip_req.msgHeader.toField; + to_addr.toParams := f_sip_param_set(to_addr.toParams, "tag", f_sip_rand_tag()); + + /* Double it and expect UAC to use it. */ + g_pars.subscr.registrar_expires := g_pars.subscr.registrar_expires * 2; + + /* Tx 423 Interval Too Brief */ + var template (value) PDU_SIP_Response tx_resp; + tx_resp := ts_SIP_Response_423_Interval_Too_Brief( + g_rx_sip_req.msgHeader.callId.callid, + g_rx_sip_req.msgHeader.fromField, + to_addr, + g_rx_sip_req.msgHeader.via, + g_rx_sip_req.msgHeader.cSeq.seqNumber, + minExpires := ts_MinExpires(int2str(g_pars.subscr.registrar_expires)), + server := g_pars.server_name, + userAgent := omit); + SIP.send(tx_resp); + + g_pars.subscr.exp_uac_expires := g_pars.subscr.registrar_expires; + as_IMS_register(); + + as_IMS_unregister(); + setverdict(pass); +} +testcase TC_ims_registration_423_interval_too_brief() runs on test_CT { + var IMS_ConnHdlrPars pars; + var IMS_ConnHdlr vc_conn; + f_init(); + pars := f_init_IMS_ConnHdlrPars(); + vc_conn := f_start_handler_IMS(refers(f_TC_ims_registration_423_interval_too_brief), pars); + + f_AMI_IMS_register(pars); + + /* Stay registered for one second */ + f_sleep(1.0); + + /* Trigger unregistration: */ + f_AMI_IMS_unregister(pars); + + vc_conn.done; + f_shutdown(); +} + +/* Test IMS re-registration based on Expires (TS 24.229 5.1.1.4.1, RFC 3261 10.3)*/ +private function f_TC_ims_reregistration(charstring id) runs on IMS_ConnHdlr { + f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.registrar_sip_record.addr))); + as_IMS_register(); + + timer Trereg; + Trereg.start(int2float(g_pars.subscr.registrar_expires) - 5.0); + g_pars.subscr.registrar_expires := c_def_expires; + alt { + [] as_IMS_2nd_register(); + [] Trereg.timeout { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str(g_name & ": Timeout waiting for re-registration")); + } + } + Trereg.stop; + + as_IMS_unregister(); + setverdict(pass); +} +testcase TC_ims_reregistration() runs on test_CT { + var IMS_ConnHdlrPars pars; + var IMS_ConnHdlr vc_conn; + f_init(); + pars := f_init_IMS_ConnHdlrPars(); + pars.subscr.registrar_expires := 30; + vc_conn := f_start_handler_IMS(refers(f_TC_ims_reregistration), pars); + + f_AMI_IMS_register(pars); + + /* Wait for Asterisk to re-register and IMS_ConnHdlr to handle it... */ + f_sleep(int2float(pars.subscr.registrar_expires)); + + /* Trigger unregistration: */ + f_AMI_IMS_unregister(pars); + + vc_conn.done; + f_shutdown(); +} + +private function f_ims_call_mo_configure(inout SIPConnHdlrPars sip_pars, inout IMS_ConnHdlrPars ims_pars, + charstring tgt_ext_msisdn := "90829") runs on test_CT { + sip_pars.cp.exp_update_to_direct_rtp := false; + sip_pars.cp.calling := sip_pars.registrar_sip_record; + sip_pars.cp.called := valueof(ts_SipAddr(ts_HostPort(sip_pars.remote_sip_host), + ts_UserInfo(tgt_ext_msisdn))); + ims_pars.subscr.cp.calling := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm), + ts_UserInfo(ims_pars.subscr.msisdn))); + ims_pars.subscr.cp.called := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm), + ts_UserInfo(tgt_ext_msisdn))); +} + +private function f_ims_call_mo_establish(inout SIPConnHdlr vc_conn_sip, inout IMS_ConnHdlr vc_conn_ims) runs on test_CT { + var AMI_Msg ami_msg; + + COORD.send(COORD_CMD_START) to vc_conn_sip; + + IMS_COORD.receive(IMS_COORD_CMD_CALL_TRYING) from vc_conn_ims; + ami_msg := f_ami_wait_rx_msg(AMI_CLIENT, + tr_AMI_Event_Newchannel(mp_volte_ims_outbound_registration), + fail_others := false); + g_asterisk_chan_name := valueof(f_ami_msg_get_value(ami_msg, AMI_FIELD_CHANNEL)); + f_ami_action_DedicatedBearerStatus(AMI_CLIENT, + g_asterisk_chan_name, + "Up", fail_others := false); + + COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn_sip; + IMS_COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn_ims; +} + +private function f_TC_ims_call_mo_IMS_ConnHdlr_register_establish_call() runs on IMS_ConnHdlr { + f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.registrar_sip_record.addr))); + if (ispresent(g_pars.subscr.cp.called)) { + f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.cp.called.addr))); + } + as_IMS_register(); + setverdict(pass); + as_IMS_mo_call_accept(); + setverdict(pass); + COORD.send(COORD_CMD_CALL_ESTABLISHED); +} + +private function f_TC_ims_call_mo_IMS_ConnHdlr_hangup_call_unregister() runs on IMS_ConnHdlr { + as_IMS_exp_call_hangup(g_pars.subscr.cp.sip_seq_nr + 1); + COORD.send(IMS_COORD_CMD_CALL_FINISHED); + setverdict(pass); + as_IMS_unregister(); +} +private function f_TC_ims_call_mo_IMS_ConnHdlr(charstring id) runs on IMS_ConnHdlr { + f_TC_ims_call_mo_IMS_ConnHdlr_register_establish_call(); + if (g_pars.subscr.cp.mo.support_timer_session_expires > 0) { + timer Trefresh; + Trefresh.start(int2float(g_pars.subscr.cp.mo.support_timer_session_expires)); + alt { + [] as_IMS_exp_call_refresh(); + [] Trefresh.timeout { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str(g_name & ": Timeout waiting for Session Refresh")); + } + } + Trefresh.stop; + /* Sleep to make sure response arrives to the peer */ + f_sleep(1.0); + COORD.send(IMS_COORD_CMD_CALL_SESSION_REFRESH); + } + f_TC_ims_call_mo_IMS_ConnHdlr_hangup_call_unregister(); +} + +private function f_TC_ims_call_mo_IMS_ConnHdlr_with_holdresume(charstring id) runs on IMS_ConnHdlr { + f_TC_ims_call_mo_IMS_ConnHdlr_register_establish_call(); + as_IMS_exp_call_hold(); + f_sleep(1.0); + as_IMS_exp_call_resume(); + f_TC_ims_call_mo_IMS_ConnHdlr_hangup_call_unregister(); +} +private function f_TC_ims_call_mo_IMS_ConnHdlr_with_holdswitchresume(charstring id) runs on IMS_ConnHdlr { + var IMS_ConnHdlrSubscrPars subscr0; + var Addr_Union called_addr; + + f_TC_ims_call_mo_IMS_ConnHdlr_register_establish_call(); + + called_addr := g_pars.subscr.cp.called.addr; + called_addr.nameAddr.addrSpec.userInfo := valueof(ts_UserInfo("123456")); + f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(called_addr))); + + as_IMS_exp_call_hold(); + + /* Backup state of 1st call subscriber: */ + subscr0 := g_pars.subscr; + g_pars.subscr.cp.called.addr := called_addr; + as_IMS_mo_call_accept(); + COORD.send(COORD_CMD_CALL_ESTABLISHED); + log ("IMS: 2nd call ongoing"); + + as_IMS_exp_call_hangup(g_pars.subscr.cp.sip_seq_nr + 1); + COORD.send(IMS_COORD_CMD_CALL_FINISHED); + /* Restore 1s call metadata before hanging it up here: */ + g_pars.subscr := subscr0; + + as_IMS_exp_call_resume(); + f_TC_ims_call_mo_IMS_ConnHdlr_hangup_call_unregister(); +} +private function f_TC_ims_call_mo_IMS_ConnHdlr_2nd_mt_rejected(charstring id) runs on IMS_ConnHdlr { + var IMS_ConnHdlrSubscrPars subscr0; + var default d_trying, d_ringing, d_sessprog; + var Via via; + + /* Establish 1st (MO) call */ + f_TC_ims_call_mo_IMS_ConnHdlr_register_establish_call(); + + /* Backup state of 1st call subscriber: */ + subscr0 := g_pars.subscr; + /* Initiate second (MT) call: */ + f_IMS_mt_call_rejected(486, "Busy Here"); + /* Leave some time for last ACK from IMS to arrive to Asterisk */ + f_sleep(1.0); + /* Restore 1s call metadata before hanging it up here: */ + g_pars.subscr := subscr0; + COORD.send(IMS_COORD_CMD_CALL_REJECTED); + + f_TC_ims_call_mo_IMS_ConnHdlr_hangup_call_unregister(); +} + +type function call_established_fn(SIPConnHdlrPars sip_pars, IMS_ConnHdlrPars ims_pars) runs on test_CT; +private function call_established_fn_sleep1(SIPConnHdlrPars sip_pars, IMS_ConnHdlrPars ims_pars) runs on test_CT { + f_sleep(1.0); +} +private function call_established_fn_wait_ims_coord_cmd_call_rejected(SIPConnHdlrPars sip_pars, IMS_ConnHdlrPars ims_pars) runs on test_CT { + IMS_COORD.receive(IMS_COORD_CMD_CALL_REJECTED); +} + +private function f_TC_ims_call_mo(boolean close_tcp_after_registration := false, + boolean use_precondition_ext := true, + boolean use_session_timer := false, + call_established_fn call_established_cb := refers(call_established_fn_sleep1), + void_fn sip_fn := refers(f_TC_internal_call_mo), + ims_void_fn ims_fn := refers(f_TC_ims_call_mo_IMS_ConnHdlr)) runs on test_CT { + var SIPConnHdlrPars sip_pars; + var IMS_ConnHdlrPars ims_pars; + var SIPConnHdlr vc_conn_sip; + var IMS_ConnHdlr vc_conn_ims; + + f_init(); + + sip_pars := f_init_ConnHdlrPars(idx := 1); + ims_pars := f_init_IMS_ConnHdlrPars(); + ims_pars.subscr.close_tcp_after_registration := close_tcp_after_registration; + + f_ims_call_mo_configure(sip_pars, ims_pars); + ims_pars.subscr.cp.support_precondition_ext := use_precondition_ext; + ims_pars.subscr.cp.require_precondition_ext := use_precondition_ext; + ims_pars.subscr.cp.mo.tx_coord_cmd_invite_trying := true; + ims_pars.subscr.cp.mo.support_timer_enable := use_session_timer; + if (use_session_timer) { + sip_pars.t_guard := 120.0; + ims_pars.t_guard := 120.0; + ims_pars.subscr.cp.mo.support_timer_exp_min_se := 90; + ims_pars.subscr.cp.mo.support_timer_session_expires := 90; + + } + + vc_conn_ims := f_start_handler_IMS(ims_fn, ims_pars); + vc_conn_sip := f_start_handler(sip_fn, sip_pars); + + COORD.receive(COORD_CMD_REGISTERED) from vc_conn_sip; + + f_AMI_IMS_register(ims_pars); + + f_ims_call_mo_establish(vc_conn_sip, vc_conn_ims); + + /* Call on-going */ + if (use_session_timer) { + IMS_COORD.receive(IMS_COORD_CMD_CALL_SESSION_REFRESH); + } + call_established_cb.apply(sip_pars, ims_pars); + + COORD.send(COORD_CMD_HANGUP) to vc_conn_sip; + IMS_COORD.receive(IMS_COORD_CMD_CALL_FINISHED) from vc_conn_ims; + + /* Notify network released dedicated bearer: */ + AMI_CLIENT.clear; + f_ami_action_DedicatedBearerStatus(AMI_CLIENT, + g_asterisk_chan_name, + "Down", fail_others := false); + + /* Trigger unregistration: */ + f_sleep(1.0); + COORD.send(COORD_CMD_UNREGISTER) to vc_conn_sip; + AMI_CLIENT.clear; + f_AMI_IMS_unregister(ims_pars); + + vc_conn_sip.done; + vc_conn_ims.done; + f_shutdown(); +} +testcase TC_ims_call_mo() runs on test_CT { + f_TC_ims_call_mo(); +} +testcase TC_ims_call_mo_session_timer() runs on test_CT { + f_TC_ims_call_mo(use_session_timer := true); +} +testcase TC_ims_call_mo_noprecondition() runs on test_CT { + f_TC_ims_call_mo(use_precondition_ext := false); +} +testcase TC_ims_call_mo_holdresume_mo() runs on test_CT { + f_TC_ims_call_mo(sip_fn := refers(f_TC_internal_call_mo_with_holdresume), + ims_fn := refers(f_TC_ims_call_mo_IMS_ConnHdlr_with_holdresume)); +} +/* TC_ims_call_mo, but IMS server closes the TCP conn used during 2nd register after ACKing it. + * Asterisk should consider it is still registered even if that TCP conn was closed, + * and MO call should work the same. + */ +testcase TC_ims_call_mo_after_tcp_conn_closed() runs on test_CT { + f_TC_ims_call_mo(close_tcp_after_registration := true); +} + +/* Test a 2nd call initiated from a local SIP UA towards IMS after having HOLD the 1st one. */ +private function call_established_fn_holdswitchresume(SIPConnHdlrPars sip_pars, IMS_ConnHdlrPars ims_pars) runs on test_CT { + var AMI_Msg ami_msg; + var charstring old_asterisk_chan_name := g_asterisk_chan_name; /* backup */ + COORD.send(COORD_CMD_START); + + IMS_COORD.receive(IMS_COORD_CMD_CALL_TRYING); + ami_msg := f_ami_wait_rx_msg(AMI_CLIENT, + tr_AMI_Event_Newchannel(mp_volte_ims_outbound_registration), + fail_others := false); + g_asterisk_chan_name := valueof(f_ami_msg_get_value(ami_msg, AMI_FIELD_CHANNEL)); + f_ami_action_DedicatedBearerStatus(AMI_CLIENT, + g_asterisk_chan_name, + "Up", fail_others := false); + + COORD.receive(COORD_CMD_CALL_ESTABLISHED); + IMS_COORD.receive(COORD_CMD_CALL_ESTABLISHED); + /* 2nd call ongoing */ + log ("test_CT: 2nd call ongoing"); + f_sleep(1.0); + /* Tear down 2nd call: */ + COORD.send(COORD_CMD_HANGUP); + IMS_COORD.receive(IMS_COORD_CMD_CALL_FINISHED); + + /* Notify network released 2nd dedicated bearer: */ + AMI_CLIENT.clear; + f_ami_action_DedicatedBearerStatus(AMI_CLIENT, + g_asterisk_chan_name, + "Down", fail_others := false); + + g_asterisk_chan_name := old_asterisk_chan_name; /* restore */ +} +testcase TC_ims_call_mo_holdswitchresume_mo() runs on test_CT { + f_TC_ims_call_mo(call_established_cb := refers(call_established_fn_holdswitchresume), + sip_fn := refers(f_TC_internal_call_mo_with_holdswitchresume), + ims_fn := refers(f_TC_ims_call_mo_IMS_ConnHdlr_with_holdswitchresume)); +} + +/* Test a 2nd call initiated from a local SIP UA towards IMS is rejected with + * 503 Service Unavailable if IMS endpoint is already busy in a call. */ +private function call_established_fn_2ndrejected(SIPConnHdlrPars sip_pars, IMS_ConnHdlrPars ims_pars) runs on test_CT { + var SIPConnHdlrPars sip_pars2; + var SIPConnHdlr vc_conn_sip2; + sip_pars2 := f_init_ConnHdlrPars(idx := 2); + f_ims_call_mo_configure(sip_pars2, ims_pars, tgt_ext_msisdn := "88888888"); + vc_conn_sip2 := f_start_handler(refers(f_TC_internal_call_mo_rejected), sip_pars2); + + COORD.receive(COORD_CMD_REGISTERED) from vc_conn_sip2; + COORD.send(COORD_CMD_START) to vc_conn_sip2; + COORD.receive(COORD_CMD_CALL_FINISHED) from vc_conn_sip2; + COORD.send(COORD_CMD_UNREGISTER) to vc_conn_sip2; + vc_conn_sip2.done; +} +testcase TC_ims_call_mo_2nd_mo_rejected() runs on test_CT { + f_TC_ims_call_mo(call_established_cb := refers(call_established_fn_2ndrejected)); +} + +/* Test a 2nd call initiated from IMS towards Astierisk is rejected with + * 486 Busy Here if IMS endpoint is already busy in a call. */ +testcase TC_ims_call_mo_2nd_mt_rejected() runs on test_CT { + f_TC_ims_call_mo(call_established_cb := refers(call_established_fn_wait_ims_coord_cmd_call_rejected), + ims_fn := refers(f_TC_ims_call_mo_IMS_ConnHdlr_2nd_mt_rejected)); +} + +/* Test MT call initiated by IMS */ +private function f_TC_ims_call_mt_IMS_ConnHdlr(charstring id) runs on IMS_ConnHdlr { + f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.registrar_sip_record.addr))); + as_IMS_register(); + COORD.send(IMS_COORD_CMD_REGISTERED); + setverdict(pass); + + COORD.receive(IMS_COORD_CMD_START); + f_IMS_mt_call_setup(); + setverdict(pass); + COORD.send(IMS_COORD_CMD_CALL_ESTABLISHED); + + COORD.receive(IMS_COORD_CMD_HANGUP); + f_IMS_do_call_hangup(); + setverdict(pass); + COORD.send(IMS_COORD_CMD_CALL_FINISHED); + + as_IMS_unregister(); +} +private function f_TC_ims_call_mt(boolean use_precondition_ext) runs on test_CT { + var SIPConnHdlrPars sip_pars; + var IMS_ConnHdlrPars ims_pars; + var SIPConnHdlr vc_conn_sip; + var IMS_ConnHdlr vc_conn_ims; + var AMI_Msg ami_msg; + const charstring c_ext_msisdn := "90829"; + + f_init(); + + sip_pars := f_init_ConnHdlrPars(idx := 1); + ims_pars := f_init_IMS_ConnHdlrPars(); + + sip_pars.cp.exp_update_to_direct_rtp := false; + sip_pars.cp.calling := valueof(ts_SipAddr(ts_HostPort(sip_pars.remote_sip_host), + ts_UserInfo(c_ext_msisdn))); + sip_pars.cp.called := sip_pars.local_sip_record; + ims_pars.subscr.cp.calling := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm), + ts_UserInfo(c_ext_msisdn))); + ims_pars.subscr.cp.called := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm), + ts_UserInfo(ims_pars.subscr.msisdn))); + ims_pars.subscr.cp.support_precondition_ext := use_precondition_ext; + ims_pars.subscr.cp.require_precondition_ext := use_precondition_ext; + ims_pars.subscr.cp.mt.tx_coord_cmd_session_progress := use_precondition_ext; + + vc_conn_ims := f_start_handler_IMS(refers(f_TC_ims_call_mt_IMS_ConnHdlr), ims_pars); + vc_conn_sip := f_start_handler(refers(f_TC_internal_call_mt), sip_pars); + + COORD.receive(COORD_CMD_REGISTERED) from vc_conn_sip; + + f_AMI_IMS_register(ims_pars); + IMS_COORD.receive(IMS_COORD_CMD_REGISTERED) from vc_conn_ims; + + IMS_COORD.send(IMS_COORD_CMD_START) to vc_conn_ims; + + ami_msg := f_ami_wait_rx_msg(AMI_CLIENT, + tr_AMI_Event_Newchannel(mp_volte_ims_outbound_registration), + fail_others := false); + g_asterisk_chan_name := valueof(f_ami_msg_get_value(ami_msg, AMI_FIELD_CHANNEL)); + if (ims_pars.subscr.cp.mt.tx_coord_cmd_session_progress) { + IMS_COORD.receive(IMS_COORD_CMD_CALL_SESSION_PROGRESS) from vc_conn_ims; + } + f_ami_action_DedicatedBearerStatus(AMI_CLIENT, + g_asterisk_chan_name, + "Up", fail_others := false); + + IMS_COORD.receive(IMS_COORD_CMD_CALL_ESTABLISHED) from vc_conn_ims; + COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn_sip; + + /* Call on-going */ + f_sleep(1.0); + + IMS_COORD.send(IMS_COORD_CMD_HANGUP) to vc_conn_ims; + + /* Notify network released dedicated bearer: */ + IMS_COORD.receive(IMS_COORD_CMD_CALL_FINISHED) from vc_conn_ims; + AMI_CLIENT.clear; + f_ami_action_DedicatedBearerStatus(AMI_CLIENT, + g_asterisk_chan_name, + "Down", fail_others := false); + COORD.receive(COORD_CMD_CALL_FINISHED) from vc_conn_sip; + + /* Trigger unregistration: */ + f_sleep(1.0); + COORD.send(COORD_CMD_UNREGISTER) to vc_conn_sip; + AMI_CLIENT.clear; + f_AMI_IMS_unregister(ims_pars); + + vc_conn_sip.done; + vc_conn_ims.done; + f_shutdown(); +} +testcase TC_ims_call_mt() runs on test_CT { + f_TC_ims_call_mt(true); +} +testcase TC_ims_call_mt_noprecondition() runs on test_CT { + f_TC_ims_call_mt(false); +} + +/* Validate incoming IMS MT call is rejected if no UAs are registered, hence not available. */ +private function f_TC_ims_call_mt_IMS_ConnHdlr_603_Decline(charstring id) runs on IMS_ConnHdlr { + f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.registrar_sip_record.addr))); + as_IMS_register(); + COORD.send(IMS_COORD_CMD_REGISTERED); + setverdict(pass); + + COORD.receive(IMS_COORD_CMD_START); + f_IMS_mt_call_rejected(603, "Decline"); + setverdict(pass); + COORD.send(IMS_COORD_CMD_CALL_REJECTED); + + as_IMS_unregister(); +} +testcase TC_ims_call_mt_no_local_uas_registered() runs on test_CT { + var IMS_ConnHdlrPars ims_pars; + var IMS_ConnHdlr vc_conn_ims; + var AMI_Msg ami_msg; + const charstring c_ext_msisdn := "90829"; + + f_init(); + + ims_pars := f_init_IMS_ConnHdlrPars(); + + ims_pars.subscr.cp.calling := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm), + ts_UserInfo(c_ext_msisdn))); + ims_pars.subscr.cp.called := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm), + ts_UserInfo(ims_pars.subscr.msisdn))); + + vc_conn_ims := f_start_handler_IMS(refers(f_TC_ims_call_mt_IMS_ConnHdlr_603_Decline), ims_pars); + + f_AMI_IMS_register(ims_pars); + IMS_COORD.receive(IMS_COORD_CMD_REGISTERED) from vc_conn_ims; + + IMS_COORD.send(IMS_COORD_CMD_START) to vc_conn_ims; + + ami_msg := f_ami_wait_rx_msg(AMI_CLIENT, + tr_AMI_Event_Newchannel(mp_volte_ims_outbound_registration), + fail_others := false); + g_asterisk_chan_name := valueof(f_ami_msg_get_value(ami_msg, AMI_FIELD_CHANNEL)); + + f_ami_action_DedicatedBearerStatus(AMI_CLIENT, + g_asterisk_chan_name, + "Up", fail_others := false); + + /* Notify network released dedicated bearer: */ + IMS_COORD.receive(IMS_COORD_CMD_CALL_REJECTED) from vc_conn_ims; + AMI_CLIENT.clear; + f_ami_action_DedicatedBearerStatus(AMI_CLIENT, + g_asterisk_chan_name, + "Down", fail_others := false); + + /* Trigger unregistration: */ + f_sleep(1.0); + AMI_CLIENT.clear; + f_AMI_IMS_unregister(ims_pars); + + vc_conn_ims.done; + f_shutdown(); +} + +/* Validate incoming IMS MT call is rejected if no UAs are available due to being busy in an internall call. */ +testcase TC_ims_call_mt_local_uas_inacall() runs on test_CT { + var SIPConnHdlrPars sip_pars[2]; + var IMS_ConnHdlrPars ims_pars; + var SIPConnHdlr vc_conn_sip[2]; + var IMS_ConnHdlr vc_conn_ims; + var AMI_Msg ami_msg; + const charstring c_ext_msisdn := "90829"; + + f_init(); + + sip_pars[0] := f_init_ConnHdlrPars(idx := 1); + sip_pars[1] := f_init_ConnHdlrPars(idx := 2); + ims_pars := f_init_IMS_ConnHdlrPars(); + + sip_pars[0].cp.calling := sip_pars[0].registrar_sip_record; + sip_pars[0].cp.called := sip_pars[1].registrar_sip_record; + + sip_pars[1].cp.calling := sip_pars[0].registrar_sip_record; + sip_pars[1].cp.called := sip_pars[1].local_sip_record; + + ims_pars.subscr.cp.calling := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm), + ts_UserInfo(c_ext_msisdn))); + ims_pars.subscr.cp.called := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm), + ts_UserInfo(ims_pars.subscr.msisdn))); + + vc_conn_sip[0] := f_start_handler(refers(f_TC_internal_call_mo), sip_pars[0]); + vc_conn_sip[1] := f_start_handler(refers(f_TC_internal_call_mt), sip_pars[1]); + + vc_conn_ims := f_start_handler_IMS(refers(f_TC_ims_call_mt_IMS_ConnHdlr_603_Decline), ims_pars); + f_AMI_IMS_register(ims_pars); + + interleave { + [] COORD.receive(COORD_CMD_REGISTERED) from vc_conn_sip[0]; + [] COORD.receive(COORD_CMD_REGISTERED) from vc_conn_sip[1]; + [] IMS_COORD.receive(IMS_COORD_CMD_REGISTERED) from vc_conn_ims; + } + + COORD.send(COORD_CMD_START) to vc_conn_sip[0]; + + interleave { + [] COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn_sip[0]; + [] COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn_sip[1]; + } + + /* Call on-going, now IMS MT call is attempted: */ + f_sleep(1.0); + IMS_COORD.send(IMS_COORD_CMD_START) to vc_conn_ims; + ami_msg := f_ami_wait_rx_msg(AMI_CLIENT, + tr_AMI_Event_Newchannel(mp_volte_ims_outbound_registration), + fail_others := false); + g_asterisk_chan_name := valueof(f_ami_msg_get_value(ami_msg, AMI_FIELD_CHANNEL)); + f_ami_action_DedicatedBearerStatus(AMI_CLIENT, + g_asterisk_chan_name, + "Up", fail_others := false); + IMS_COORD.receive(IMS_COORD_CMD_CALL_REJECTED) from vc_conn_ims; + AMI_CLIENT.clear; + f_ami_action_DedicatedBearerStatus(AMI_CLIENT, + g_asterisk_chan_name, + "Down", fail_others := false); + + + COORD.send(COORD_CMD_HANGUP) to vc_conn_sip[0]; + COORD.receive(COORD_CMD_CALL_FINISHED) from vc_conn_sip[1]; + + COORD.send(COORD_CMD_UNREGISTER) to vc_conn_sip[0]; + COORD.send(COORD_CMD_UNREGISTER) to vc_conn_sip[1]; + AMI_CLIENT.clear; + f_AMI_IMS_unregister(ims_pars); + + vc_conn_sip[0].done; + vc_conn_sip[1].done; + vc_conn_ims.done; + f_shutdown(); +} + +control { + execute( TC_internal_registration() ); + execute( TC_internal_call_momt() ); + execute( TC_internal_call_all_2registered() ); + execute( TC_internal_call_all_3registered() ); + execute( TC_internal_call_all_4registered() ); + execute( TC_ims_registration() ); + execute( TC_ims_registration_resync() ); + execute( TC_ims_registration_timeout_initial_100Trying() ); + execute( TC_ims_registration_timeout_initial_401Unauthorized() ); + execute( TC_ims_registration_timeout_resync_401Unauthorized() ); + execute( TC_ims_registration_timeout_protected_100Trying() ); + execute( TC_ims_registration_timeout_protected_200OK() ); + execute( TC_ims_registration_423_interval_too_brief() ); + execute( TC_ims_reregistration() ); + execute( TC_ims_call_mo() ); + execute( TC_ims_call_mo_session_timer() ); + execute( TC_ims_call_mo_noprecondition() ); + execute( TC_ims_call_mo_holdresume_mo() ); + execute( TC_ims_call_mo_holdswitchresume_mo() ); + execute( TC_ims_call_mo_2nd_mo_rejected() ); + execute( TC_ims_call_mo_2nd_mt_rejected() ); + execute( TC_ims_call_mo_after_tcp_conn_closed() ); + execute( TC_ims_call_mt() ); + execute( TC_ims_call_mt_noprecondition() ); + execute( TC_ims_call_mt_no_local_uas_registered() ); + execute( TC_ims_call_mt_local_uas_inacall() ); +} + +} |