osmo-ttcn3-hacks/bsc/BSC_Tests_LCLS.ttcn

763 lines
25 KiB
Plaintext
Raw Permalink Normal View History

module BSC_Tests_LCLS {
/* Integration Tests for OsmoBSC
* (C) 2018 by Harald Welte <laforge@gnumonks.org>
* All rights reserved.
*
* 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
*
* This test suite tests OsmoBSC while emulating both multiple BTS + MS as
* well as the MSC. See README for more details.
*
* There are test cases that run in so-called 'handler mode' and test cases
* that run directly on top of the BSSAP and RSL CodecPorts. The "handler mode"
* tests abstract the multiplexing/demultiplexing of multiple SCCP connections
* and/or RSL channels and are hence suitable for higher-level test cases, while
* the "raw" tests directly on top of the CodecPorts are more suitable for lower-
* level testing.
*/
import from General_Types all;
import from Osmocom_Types all;
import from GSM_Types all;
import from IPL4asp_Types all;
import from BSSAP_Types all;
import from RAN_Adapter all;
import from BSSAP_CodecPort all;
import from BSSMAP_Templates all;
import from IPA_Emulation all;
import from IPA_CodecPort all;
import from IPA_Types all;
import from RSL_Types all;
import from RSL_Emulation all;
import from MGCP_Types all;
import from MGCP_Emulation all;
import from MGCP_Templates all;
import from SDP_Types all;
import from SDP_Templates all;
import from Native_Functions all;
import from Osmocom_CTRL_Functions all;
import from Osmocom_CTRL_Types all;
import from Osmocom_CTRL_Adapter all;
import from Osmocom_VTY_Functions all;
import from TELNETasp_PortType all;
import from MobileL3_CommonIE_Types all;
import from MobileL3_Types all;
import from L3_Templates all;
import from GSM_RR_Types all;
import from BSSMAP_Templates all;
import from RAN_Emulation all;
import from MSC_ConnectionHandler all;
import from BSC_Tests all;
/* The philosophy of this testsuite is to re-use as much as possible the existing components
* and functions that we have in BSC_Tests and its dependencies. However, as opposed to those
* normal BSC tests, we here have to run *two* ConnHdlr and synchronize activity between them.
*
* We do this by adding some special-purpose ports between the main test component running the
* test case [lcls_]test_CT and the per-connection [LCLS_]MSC_ConnHdlr.
*/
/* take test_CT from BSC_Tests and extend it with LCLS specific bits */
type component lcls_test_CT extends test_CT {
/* Component references */
var LCLS_MSC_ConnHdlr vc_CONN_A;
var LCLS_MSC_ConnHdlr vc_CONN_B;
/* Ports to the two call legs */
port LCLS_InterComp_PT CONN_A;
port LCLS_InterComp_PT CONN_B;
}
/* take MSC_ConnHdlr and extend it with LCLS specific bits */
type component LCLS_MSC_ConnHdlr extends MSC_ConnHdlr {
/* Port back to the controlling lcls_test_CT */
port LCLS_InterComp_PT MASTER;
}
/* port type between lcls_test_CT and LCLS_MSC_ConnHdlr */
type port LCLS_InterComp_PT message {
/* BSSAP from BSSA_ConnHdlr */
inout PDU_BSSAP, RAN_Conn_Prim, PDU_DTAP_MO, PDU_DTAP_MT,
/* RSL from RSL_DchanHdlr */
RSLDC_ChanRqd, RSL_Message,
/* MGCP from MGCP_ConnHdlr */
MgcpCommand, MgcpResponse,
LclsCompSync;
} with { extension "internal" };
type enumerated LclsCompSync {
/* ConnHdlr signals to master component that assignment has completed */
LCLS_COMP_SYNC_ASS_COMPL
}
/* forward messages between the RSL/MGCP/BSSAP Emulation and the master component */
private altstep as_lcls_conn_hdlr_proxy() runs on LCLS_MSC_ConnHdlr {
var PDU_BSSAP bssap;
var RAN_Conn_Prim bssap_p;
var PDU_DTAP_MO dtap_mo;
var PDU_DTAP_MT dtap_mt;
var MgcpCommand mgcp_cmd;
var MgcpResponse mgcp_rsp;
var RSL_Message rsl_msg;
/* from ConnHdlr to master process */
[] BSSAP.receive(PDU_BSSAP:?) -> value bssap { MASTER.send(bssap); }
[] BSSAP.receive(RAN_Conn_Prim:?) -> value bssap_p { MASTER.send(bssap_p); }
[] BSSAP.receive(PDU_DTAP_MO:?) -> value dtap_mo { MASTER.send(dtap_mo); }
[] BSSAP.receive(PDU_DTAP_MT:?) -> value dtap_mt { MASTER.send(dtap_mt); }
[] MGCP.receive(MgcpCommand:?) -> value mgcp_cmd { MASTER.send(mgcp_cmd); }
[] MGCP.receive(MgcpResponse:?) -> value mgcp_rsp { MASTER.send(mgcp_rsp); }
[] RSL.receive(RSL_Message:?) -> value rsl_msg { MASTER.send(rsl_msg); }
/* from master process to ConnHdlr */
[] MASTER.receive(PDU_BSSAP:?) -> value bssap { BSSAP.send(bssap); }
[] MASTER.receive(RAN_Conn_Prim:?) -> value bssap_p { BSSAP.send(bssap_p); }
[] MASTER.receive(PDU_DTAP_MO:?) -> value dtap_mo { BSSAP.send(dtap_mo); }
[] MASTER.receive(PDU_DTAP_MT:?) -> value dtap_mt { BSSAP.send(dtap_mt); }
[] MASTER.receive(MgcpCommand:?) -> value mgcp_cmd { MGCP.send(mgcp_cmd); }
[] MASTER.receive(MgcpResponse:?) -> value mgcp_rsp { MGCP.send(mgcp_rsp); }
[] MASTER.receive(RSL_Message:?) -> value rsl_msg { RSL.send(rsl_msg); }
}
private function f_lcls_connhdlr_main(charstring id) runs on LCLS_MSC_ConnHdlr {
/* 1) establish the connection between RSL and BSSAP side */
var PDU_BSSAP ass_req := f_gen_ass_req();
var template PDU_BSSAP ass_compl := f_gen_exp_compl();
ass_req.pdu.bssmap.assignmentRequest.codecList := g_pars.ass_codec_list;
ass_req.pdu.bssmap.assignmentRequest.channelType :=
f_BSSMAP_chtype_from_codec(g_pars.ass_codec_list.codecElements[0]);
f_establish_fully(ass_req, ass_compl);
/* 2) notify master that assignment has completed */
MASTER.send(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL);
/* 3) proxy packets between master component and various ports */
while (true) {
as_lcls_conn_hdlr_proxy();
}
}
type function lcls_void_fn(charstring id) runs on LCLS_MSC_ConnHdlr;
/* first function inside ConnHdlr component; sets g_pars + starts function */
private function f_handler_init(lcls_void_fn fn, charstring id, template (omit) TestHdlrParams pars := omit)
runs on LCLS_MSC_ConnHdlr {
if (isvalue(pars)) {
g_pars := valueof(pars);
}
fn.apply(id);
}
/* helper function to create and connect a MSC_ConnHdlr component */
BSC_Tests: add support for per-TRX RSL connections So far it was possible to communicate with multiple BTS instances, however we were limited to only one IPA/RSL connection per BTS. Communicating with additional TRX instances requires establishing additional per-TRX IPA/RSL connections. This patch extends the testsuite infrastructure, so that it becomes possible to have up to 4 individual TRX instances for each BTS. * Introduce record 'BtsParams', store per-BTS params in an array. * IPA_RSL_PT: test_CT.IPA_RSL[NUM_BTS] -> test_CT.IPA_RSL[NUM_BTS][NUM_TRX]. * BTS_State: test_CT.bts[NUM_BTS] -> test_CT.bts[NUM_BTS][NUM_TRX]. * f_init_bts_and_check_sysinfo() accepts number of TRX instances. * f_init_bts() accepts number of TRX instances to configure. * f_ipa_rsl_start() accepts a BtsTrxIdx param. * Introduce record BtsTrxIdx allowing to specify BTS/TRX index values. * f_ipa_tx(), f_exp_ipa_rx() accept a BtsTrxIdx param (default {0, 0}). * f_est_dchan[_dyn]() accept a BtsTrxIdx param (default {0, 0}). ** and store the given BtsTrxIdx in returned DchanTuple. * f_chreq_act_ack() accepts a BtsTrxIdx param (default {0, 0}). * f_expect_chan_rel() accepts a BtsTrxIdx param (default {0, 0}). * f_exp_ipa_rx_nonfatal() accepts BtsTrxIdx (default {0, 0}). * f_exp_chan_rel_and_clear() uses BtsTrxIdx indicated in the given DchanTuple. * Fix CTRL/statsd expectations in TC_stat_num_bts_connected_[123]. * Fix CTRL expectations in TC_ctrl_trx_rf_locked. The major limitation is that MSC_ConnHdlr components still have no access to additional TRX instances - this can be implemented later. Change-Id: Ic0fd97234464ef624010a5f01189aa61f3393b84 Related: SYS#5460
2022-05-29 10:55:42 +00:00
/* FIXME: Why can't we use BSC_Tests.f_connect_andler() ?!?
* TODO: allow connecting to TRX1..N, not only TRX0 */
private function f_connect_handler(inout LCLS_MSC_ConnHdlr vc_conn, integer bssap_idx := 0, integer mgwpool_idx := 0) runs on lcls_test_CT {
connect(vc_conn:RAN, g_bssap[bssap_idx].vc_RAN:PROC);
BSC_Tests: add support for per-TRX RSL connections So far it was possible to communicate with multiple BTS instances, however we were limited to only one IPA/RSL connection per BTS. Communicating with additional TRX instances requires establishing additional per-TRX IPA/RSL connections. This patch extends the testsuite infrastructure, so that it becomes possible to have up to 4 individual TRX instances for each BTS. * Introduce record 'BtsParams', store per-BTS params in an array. * IPA_RSL_PT: test_CT.IPA_RSL[NUM_BTS] -> test_CT.IPA_RSL[NUM_BTS][NUM_TRX]. * BTS_State: test_CT.bts[NUM_BTS] -> test_CT.bts[NUM_BTS][NUM_TRX]. * f_init_bts_and_check_sysinfo() accepts number of TRX instances. * f_init_bts() accepts number of TRX instances to configure. * f_ipa_rsl_start() accepts a BtsTrxIdx param. * Introduce record BtsTrxIdx allowing to specify BTS/TRX index values. * f_ipa_tx(), f_exp_ipa_rx() accept a BtsTrxIdx param (default {0, 0}). * f_est_dchan[_dyn]() accept a BtsTrxIdx param (default {0, 0}). ** and store the given BtsTrxIdx in returned DchanTuple. * f_chreq_act_ack() accepts a BtsTrxIdx param (default {0, 0}). * f_expect_chan_rel() accepts a BtsTrxIdx param (default {0, 0}). * f_exp_ipa_rx_nonfatal() accepts BtsTrxIdx (default {0, 0}). * f_exp_chan_rel_and_clear() uses BtsTrxIdx indicated in the given DchanTuple. * Fix CTRL/statsd expectations in TC_stat_num_bts_connected_[123]. * Fix CTRL expectations in TC_ctrl_trx_rf_locked. The major limitation is that MSC_ConnHdlr components still have no access to additional TRX instances - this can be implemented later. Change-Id: Ic0fd97234464ef624010a5f01189aa61f3393b84 Related: SYS#5460
2022-05-29 10:55:42 +00:00
connect(vc_conn:RSL, bts[0][0].rsl.vc_RSL:CLIENT_PT);
connect(vc_conn:RSL_PROC, bts[0][0].rsl.vc_RSL:RSL_PROC);
if (isvalue(bts[1][0])) {
connect(vc_conn:RSL1, bts[1][0].rsl.vc_RSL:CLIENT_PT);
connect(vc_conn:RSL1_PROC, bts[1][0].rsl.vc_RSL:RSL_PROC);
}
connect(vc_conn:BSSAP, g_bssap[bssap_idx].vc_RAN:CLIENT);
connect(vc_conn:MGCP_PROC, vc_MGCP[mgwpool_idx]:MGCP_PROC);
connect(vc_conn:MGCP, vc_MGCP[mgwpool_idx]:MGCP_CLIENT);
}
/* function creating the two ConnHdlrs, connecting them + starting them */
private function f_lcls_test_init(TestHdlrParams pars_a, TestHdlrParams pars_b) runs on lcls_test_CT {
var charstring id_a := testcasename() & "-A";
var charstring id_b := testcasename() & "-B";
pars_b.imsi := '002029876543210'H;
pars_b.media_nr := 2;
/* create and connect the two ConnHandlers */
vc_CONN_A := LCLS_MSC_ConnHdlr.create(id_a);
f_connect_handler(vc_CONN_A);
connect(vc_CONN_A:MASTER, self:CONN_A);
vc_CONN_B := LCLS_MSC_ConnHdlr.create(id_b);
f_connect_handler(vc_CONN_B);
connect(vc_CONN_B:MASTER, self:CONN_B);
/* start the two components */
vc_CONN_A.start(f_handler_init(refers(f_lcls_connhdlr_main), id_a, pars_a));
f_sleep(3.0);
vc_CONN_B.start(f_handler_init(refers(f_lcls_connhdlr_main), id_b, pars_b));
}
private function f_lcls_test_fini() runs on lcls_test_CT {
vc_CONN_A.stop;
vc_CONN_B.stop;
}
/* ignore some messages which we're not interested in evaluating (yet) */
private altstep as_ignore() runs on lcls_test_CT {
[] CONN_A.receive(tr_DLCX) { repeat; }
[] CONN_B.receive(tr_DLCX) { repeat; }
}
/* fail if any notify is being received */
private altstep as_fail_on_lcls_notify() runs on lcls_test_CT
{
[] CONN_A.receive(tr_BSSMAP_LclsNotification(?, *)) {
setverdict(fail, "Unexpected BSSMAP LCLS Notification");
}
[] CONN_B.receive(tr_BSSMAP_LclsNotification(?, *)) {
setverdict(fail, "Unexpected BSSMAP LCLS Notification");
}
}
private function f_wait_fail_notify() runs on lcls_test_CT
{
timer T := 3.0;
T.start;
alt {
[] as_fail_on_lcls_notify();
[] T.timeout { }
}
}
private function f_lcls_init(boolean bts_mode := false, integer nr_bts := 1) runs on lcls_test_CT
{
var default d;
d := activate(as_ignore());
f_init(nr_bts, true);
f_sleep(1.0);
f_init_vty();
if (bts_mode == true) {
f_vty_config(BSCVTY, "msc", "lcls-mode bts-loop");
} else {
f_vty_config(BSCVTY, "msc", "lcls-mode mgw-loop");
}
}
/* Send an ASSIGNMENT REQ with LCLS GCR only, without LCLS CFG or CSC */
testcase TC_lcls_gcr_only() runs on lcls_test_CT {
var TestHdlrParams pars := f_gen_test_hdlr_pars();
var MSC_ConnHdlr vc_conn;
f_lcls_init();
pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR}));
pars.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O));
/* Expect LCLS status to be not reported, as no LCLS config was signalled */
pars.lcls.exp_sts := omit;
f_lcls_test_init(pars, pars);
CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL);
CONN_B.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL);
f_wait_fail_notify();
f_lcls_test_fini();
}
private function f_tc_lcls_recv_ls_exp_mgcp() runs on lcls_test_CT {
var MgcpCommand mgcp_cmd;
interleave {
[] CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_not_yet_ls));
[] CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_locally_switched));
[] CONN_B.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL);
[] CONN_A.receive(tr_MDCX) -> value mgcp_cmd {
CONN_A.send(f_build_mdcx_rsp(mgcp_cmd));
}
/* not needed, as this MDCX is still handled within MSC_ConnectionHandler
[] CONN_B.receive(tr_MDCX) -> value mgcp_cmd {
CONN_B.send(f_build_mdcx_rsp(mgcp_cmd));
}
*/
}
}
private function f_tc_lcls_ack_rsl_mdcx(RSL_Message rsl_msg, boolean send_on_a) runs on lcls_test_CT {
var boolean fixme_unused;
var RSL_IE_Body ie;
var RslChannelNr chan_nr;
var uint16_t conn_id;
var uint7_t rtp_pt := 0;
var HostName host;
var PortNumber port_num;
if (f_rsl_find_ie(rsl_msg, RSL_IE_CHAN_NR, ie) == true) {
chan_nr := ie.chan_nr;
} else {
log("Unable to find chan# in ", rsl_msg);
}
fixme_unused := f_rsl_find_ie(rsl_msg, RSL_IE_IPAC_CONN_ID, ie);
conn_id := ie.ipa_conn_id;
/* mandatory fields */
fixme_unused := f_rsl_find_ie(rsl_msg, RSL_IE_IPAC_REMOTE_IP, ie);
host := f_inet_ntoa(ie.ipa_remote_ip);
fixme_unused := f_rsl_find_ie(rsl_msg, RSL_IE_IPAC_REMOTE_PORT, ie);
port_num := ie.ipa_remote_port;
log("LCLS IPA MDCX for lchan ", chan_nr, " connection ID ", conn_id, " host ", host, ":", port_num);
/* optional */
if (f_rsl_find_ie(rsl_msg, RSL_IE_IPAC_RTP_PAYLOAD, ie)) {
rtp_pt := ie.ipa_rtp_pt;
}
if (send_on_a == true) {
CONN_A.send(ts_RSL_IPA_MDCX_ACK(chan_nr, conn_id, f_inet_addr(host), port_num, rtp_pt));
} else {
CONN_B.send(ts_RSL_IPA_MDCX_ACK(chan_nr, conn_id, f_inet_addr(host), port_num, rtp_pt));
}
}
private function f_tc_lcls_recv_ls_exp_rsl() runs on lcls_test_CT {
var RSL_Message rsl_msg;
interleave {
[] CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_not_yet_ls)) {}
[] CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_locally_switched)) {}
[] CONN_B.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL) {}
[] CONN_A.receive(tr_RSL_IPA_MDCX(?, ?)) -> value rsl_msg {
f_tc_lcls_ack_rsl_mdcx(rsl_msg, true)
}
}
}
private function f_tc_lcls_gcr_bway_connect(boolean hr, boolean bts_mode := false) runs on lcls_test_CT {
var TestHdlrParams pars_a := f_gen_test_hdlr_pars();
var TestHdlrParams pars_b;
var MSC_ConnHdlr vc_conn;
f_lcls_init(bts_mode);
if (hr == true) {
pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecHR}));
} else {
pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR}));
}
pars_a.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O));
pars_a.lcls.cfg := LCLS_CFG_both_way;
pars_a.lcls.csc := LCLS_CSC_connect;
pars_a.lcls.adjust_cx_exp := not bts_mode;
pars_b := pars_a;
/* first call is not possible to be LS (no second leg yet) */
pars_a.lcls.exp_sts := LCLS_STS_not_possible_ls;
/* second call should then reuslt in LS */
pars_b.lcls.exp_sts := LCLS_STS_locally_switched;
f_lcls_test_init(pars_a, pars_b);
CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL);
if (bts_mode == true) {
f_tc_lcls_recv_ls_exp_rsl();
} else {
f_tc_lcls_recv_ls_exp_mgcp();
}
f_lcls_test_fini();
}
/* Send an ASSIGNMENT REQ with LCLS GCR+CFG+CSC; expect connect both-way (full rate)*/
testcase TC_lcls_gcr_bway_connect() runs on lcls_test_CT {
f_tc_lcls_gcr_bway_connect(false)
}
/* Send an ASSIGNMENT REQ with LCLS GCR+CFG+CSC; expect connect both-way (half rate) */
testcase TC_lcls_gcr_bway_connect_hr() runs on lcls_test_CT {
f_tc_lcls_gcr_bway_connect(true)
}
/* BTS-loop: send an ASSIGNMENT REQ with LCLS GCR+CFG+CSC; expect connect both-way (full rate)*/
testcase TC_lcls_bts_gcr_bway_connect() runs on lcls_test_CT {
f_tc_lcls_gcr_bway_connect(false, true)
}
/* BTS-loop: send an ASSIGNMENT REQ with LCLS GCR+CFG+CSC; expect connect both-way (half rate) */
testcase TC_lcls_bts_gcr_bway_connect_hr() runs on lcls_test_CT {
f_tc_lcls_gcr_bway_connect(true, true)
}
/* Unless explicitly enabled, osmo-bsc will avoid LCLSs when the codecs or rates
* of both legs are different */
testcase TC_lcls_gcr_bway_codec_mismatch() runs on lcls_test_CT {
var TestHdlrParams pars_a := f_gen_test_hdlr_pars();
var TestHdlrParams pars_b;
var MSC_ConnHdlr vc_conn;
var MgcpCommand mgcp_cmd;
f_lcls_init();
/* First call leg uses full rate */
pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR}));
pars_a.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O));
pars_a.lcls.cfg := LCLS_CFG_both_way;
pars_a.lcls.csc := LCLS_CSC_connect;
/* The second call leg uses half-rate */
pars_b := pars_a;
pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecHR}));
/* first call is not possible to be LS (no second leg yet) */
pars_a.lcls.exp_sts := LCLS_STS_not_possible_ls;
/* second call is also not possible to be LS (codec/rate does not match) */
pars_b.lcls.exp_sts := LCLS_STS_not_yet_ls;
f_lcls_test_init(pars_a, pars_b);
interleave {
[] CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL);
[] CONN_B.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL);
[] CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_not_yet_ls));
}
f_lcls_test_fini();
}
/* Send an ASSIGNMENT REQ with LCLS CFG+CSC enabling LCLS but GCR doesn't match! */
testcase TC_lcls_gcr_nomatch_bway_connect() runs on lcls_test_CT {
var TestHdlrParams pars_a := f_gen_test_hdlr_pars();
var TestHdlrParams pars_b;
var MSC_ConnHdlr vc_conn;
var MgcpCommand mgcp_cmd;
f_lcls_init();
pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR}));
pars_a.lcls.cfg := LCLS_CFG_both_way;
pars_a.lcls.csc := LCLS_CSC_connect;
pars_a.lcls.exp_sts := LCLS_STS_not_possible_ls;
pars_b := pars_a;
/* first call is not possible to be LS (no second leg yet) */
pars_a.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O));
pars_b.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090b'O));
f_lcls_test_init(pars_a, pars_b);
CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL);
CONN_B.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL);
f_wait_fail_notify();
f_lcls_test_fini();
}
/* check for the cases where LCLS is not possible due to some reason */
private function f_lcls_not_yet_ls() runs on lcls_test_CT {
CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL);
CONN_B.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL);
CONN_A.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_not_yet_ls));
}
/* Send an ASSIGNMENT REQ with LCLS GCR+CFG+CSC; expect no connect */
testcase TC_lcls_gcr_bway_dont_connect() runs on lcls_test_CT {
var TestHdlrParams pars_a := f_gen_test_hdlr_pars();
var TestHdlrParams pars_b;
var MSC_ConnHdlr vc_conn;
f_lcls_init();
pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR}));
pars_a.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O));
pars_a.lcls.cfg := LCLS_CFG_both_way;
pars_a.lcls.csc := LCLS_CSC_do_not_connect;
pars_b := pars_a;
/* first call is not possible to be LS (no second leg yet) */
pars_a.lcls.exp_sts := LCLS_STS_not_possible_ls;
/* Expect LCLS is *NOT* established */
pars_b.lcls.exp_sts := LCLS_STS_not_yet_ls;
f_lcls_test_init(pars_a, pars_b);
f_lcls_not_yet_ls();
f_wait_fail_notify();
f_lcls_test_fini();
}
/* Send an ASSIGNMENT REQ with LCLS GCR+CFG+CSC; expect no connect */
testcase TC_lcls_gcr_unsuppported_cfg() runs on lcls_test_CT {
var TestHdlrParams pars := f_gen_test_hdlr_pars();
var MSC_ConnHdlr vc_conn;
f_lcls_init();
pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR}));
pars.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O));
pars.lcls.cfg := LCLS_CFG_both_way_and_send_DL;
pars.lcls.csc := LCLS_CSC_connect;
/* Expect LCLS is *NOT* established with "LCLS_STS_req_lcls_not_supp" */
pars.lcls.exp_sts := LCLS_STS_req_lcls_not_supp;
f_lcls_test_init(pars, pars);
CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL);
CONN_B.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL);
f_wait_fail_notify();
f_lcls_test_fini();
}
/* Send an ASSIGNMENT REQ with LCLS GCR+CFG+CSC; expect no connect */
testcase TC_lcls_gcr_unsuppported_csc() runs on lcls_test_CT {
var TestHdlrParams pars_a := f_gen_test_hdlr_pars();
var TestHdlrParams pars_b;
var MSC_ConnHdlr vc_conn;
f_lcls_init();
pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR}));
pars_a.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O));
pars_a.lcls.cfg := LCLS_CFG_both_way;
pars_a.lcls.csc := LCLS_CSC_bicast_UL_and_recv_DL_at_handover;
pars_b := pars_a;
/* first call is not possible to be LS (no second leg yet) */
pars_a.lcls.exp_sts := LCLS_STS_not_possible_ls;
/* Expect LCLS is *NOT* established */
pars_b.lcls.exp_sts := LCLS_STS_not_yet_ls;
f_lcls_test_init(pars_a, pars_b);
f_lcls_not_yet_ls();
f_lcls_test_fini();
}
/* Expect given LCLS status alongside with corresponding MDCX commands */
private function f_lcls_sts_mgcp(BIT4 expected_status) runs on lcls_test_CT {
var MgcpCommand mgcp_cmd;
interleave {
[] CONN_B.receive(tr_BSSMAP_LclsConnCtrlAck(tr_BSSMAP_IE_LclsSts(expected_status)));
[] CONN_A.receive(tr_BSSMAP_LclsNotificationSts(expected_status));
[] CONN_A.receive(tr_MDCX) -> value mgcp_cmd {
CONN_A.send(f_build_mdcx_rsp(mgcp_cmd));
}
[] CONN_B.receive(tr_MDCX) -> value mgcp_cmd {
CONN_B.send(f_build_mdcx_rsp(mgcp_cmd));
}
}
}
private function f_lcls_sts_rsl(BIT4 expected_status) runs on lcls_test_CT {
var RSL_Message rsl_msg;
interleave {
[] CONN_B.receive(tr_BSSMAP_LclsConnCtrlAck(tr_BSSMAP_IE_LclsSts(expected_status)));
[] CONN_A.receive(tr_BSSMAP_LclsNotificationSts(expected_status));
/*
[] CONN_A.receive(RSL_Message:?) -> value rsl_msg {
log("f_lcls_sts_rsl CONN_A top RSL is ", rsl_msg)
}
Ex. placeholder to catch any RSL message */
[] CONN_A.receive(tr_RSL_IPA_MDCX(?, ?)) -> value rsl_msg {
f_tc_lcls_ack_rsl_mdcx(rsl_msg, true)
}
[] CONN_B.receive(tr_RSL_IPA_MDCX(?, ?)) -> value rsl_msg {
f_tc_lcls_ack_rsl_mdcx(rsl_msg, false)
}
}
}
/* Send an ASSIGNMENT REQ with "do not connect" and enable later using LCLS CTRL */
testcase TC_lcls_gcr_bway_dont_connect_csc() runs on lcls_test_CT {
var TestHdlrParams pars_a := f_gen_test_hdlr_pars();
var TestHdlrParams pars_b;
var MSC_ConnHdlr vc_conn;
var MgcpCommand mgcp_cmd;
f_lcls_init();
pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR}));
pars_a.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O));
pars_a.lcls.cfg := LCLS_CFG_both_way;
pars_a.lcls.csc := LCLS_CSC_do_not_connect;
pars_b := pars_a;
/* first call is not possible to be LS (no second leg yet) */
pars_a.lcls.exp_sts := LCLS_STS_not_possible_ls;
/* Expect LCLS is *NOT* established */
pars_b.lcls.exp_sts := LCLS_STS_not_yet_ls;
/* start call and expect it to be "not yet" LS */
f_lcls_test_init(pars_a, pars_b);
f_lcls_not_yet_ls();
f_sleep(2.0);
/* send "connect" on A side, expect call to remain in "not yet" */
CONN_A.send(ts_BSSMAP_LclsConnCtrl(omit, ts_BSSMAP_IE_LclsCsc(LCLS_CSC_connect)));
CONN_A.receive(tr_BSSMAP_LclsConnCtrlAck(tr_BSSMAP_IE_LclsSts(LCLS_STS_not_yet_ls)));
f_sleep(2.0);
/* send "connect" on B side, expect call to go LS, with notify to A side */
CONN_B.send(ts_BSSMAP_LclsConnCtrl(omit, ts_BSSMAP_IE_LclsCsc(LCLS_CSC_connect)));
f_lcls_sts_mgcp(LCLS_STS_locally_switched);
f_wait_fail_notify();
f_lcls_test_fini();
}
private function f_build_mdcx_rsp(MgcpCommand mdcx) return MgcpResponse
{
var MgcpConnectionId conn_id := f_MgcpCmd_extract_conn_id(mdcx);
var SDP_Message sdp_in := mdcx.sdp;
var MgcpResponse resp;
var SDP_Message sdp_out;
var integer rtp_pt := str2int(sdp_in.media_list[0].media_field.fmts[0]);
sdp_out := valueof(ts_SDP(sdp_in.connection.conn_addr.addr, sdp_in.connection.conn_addr.addr,
"foo", "21", sdp_in.media_list[0].media_field.ports.port_number,
{ int2str(rtp_pt) },
{ valueof(ts_SDP_rtpmap(rtp_pt, "AMR/8000")),
valueof(ts_SDP_ptime(20)) } ));
return valueof(ts_MDCX_ACK(mdcx.line.trans_id, conn_id, sdp_out));
}
private function f_lcls_connect_break(boolean bts_mode := false) runs on lcls_test_CT {
var TestHdlrParams pars_a := f_gen_test_hdlr_pars();
var TestHdlrParams pars_b;
var MSC_ConnHdlr vc_conn;
f_lcls_init(bts_mode);
pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR}));
pars_a.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O));
pars_a.lcls.cfg := LCLS_CFG_both_way;
pars_a.lcls.csc := LCLS_CSC_connect;
pars_a.lcls.adjust_cx_exp := not bts_mode;
pars_b := pars_a;
/* first call is not possible to be LS (no second leg yet) */
pars_a.lcls.exp_sts := LCLS_STS_not_possible_ls;
/* second call should then result in LS */
pars_b.lcls.exp_sts := LCLS_STS_locally_switched;
/* Expect LS to be established successfully */
f_lcls_test_init(pars_a, pars_b);
CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL);
if (bts_mode == true) {
f_tc_lcls_recv_ls_exp_rsl();
} else {
f_tc_lcls_recv_ls_exp_mgcp();
}
/* request LS release on "A" side; call continues to be locally switched */
CONN_A.send(ts_BSSMAP_LclsConnCtrl(omit, ts_BSSMAP_IE_LclsCsc(LCLS_CSC_release_lcls)));
CONN_A.receive(tr_BSSMAP_LclsConnCtrlAck(tr_BSSMAP_IE_LclsSts(LCLS_STS_locally_switched)));
f_sleep(2.0);
/* request LS release on "B" side; call LS is released */
CONN_B.send(ts_BSSMAP_LclsConnCtrl(omit, ts_BSSMAP_IE_LclsCsc(LCLS_CSC_release_lcls)));
if (bts_mode == true) {
f_lcls_sts_rsl(LCLS_STS_no_longer_ls);
} else {
f_lcls_sts_mgcp(LCLS_STS_no_longer_ls);
}
f_lcls_test_fini();
}
/* Establish LCLS "connect" followed by a MSC-initiated break */
testcase TC_lcls_connect_break() runs on lcls_test_CT {
f_lcls_connect_break()
}
testcase TC_lcls_bts_connect_break() runs on lcls_test_CT {
f_lcls_connect_break(true)
}
/* Establish LCLS "connect" followed by a SCCP-level release of one leg */
testcase TC_lcls_connect_clear() runs on lcls_test_CT {
var TestHdlrParams pars_a := f_gen_test_hdlr_pars();
var TestHdlrParams pars_b;
var MSC_ConnHdlr vc_conn;
var MgcpCommand mgcp_cmd;
var RSL_Message rsl;
f_lcls_init();
pars_a.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR}));
pars_a.lcls.gcr := valueof(ts_GCR('010203'O, '0405'O, '060708090a'O));
pars_a.lcls.cfg := LCLS_CFG_both_way;
pars_a.lcls.csc := LCLS_CSC_connect;
pars_b := pars_a;
/* first call is not possible to be LS (no second leg yet) */
pars_a.lcls.exp_sts := LCLS_STS_not_possible_ls;
/* second call should then reuslt in LS */
pars_b.lcls.exp_sts := LCLS_STS_locally_switched;
/* Expect LS to be established successfully */
f_lcls_test_init(pars_a, pars_b);
CONN_A.receive(LclsCompSync:LCLS_COMP_SYNC_ASS_COMPL);
f_tc_lcls_recv_ls_exp_mgcp();
/* Perform hard BSSMAP Clear on "A" side, expect no LS on "B" side */
var myBSSMAP_Cause cause_val := GSM0808_CAUSE_CALL_CONTROL;
CONN_A.send(ts_BSSMAP_ClearCommand(enum2int(cause_val)));
interleave {
[] CONN_A.receive(tr_RSL_DATA_REQ(?, tr_RslLinkID_DCCH(0), decmatch tr_RRM_RR_RELEASE));
[] CONN_A.receive(tr_RSL_DEACT_SACCH(?));
[] CONN_A.receive(tr_RSL_RF_CHAN_REL(?)) -> value rsl {
var RSL_IE_Body ieb;
if (f_rsl_find_ie(rsl, RSL_IE_CHAN_NR, ieb) == true) {
CONN_A.send(ts_RSL_RF_CHAN_REL_ACK(ieb.chan_nr));
} else {
log("Unable to find chan# in RSL_RF_CHAN_REL")
}
}
[] CONN_A.receive(tr_BSSMAP_ClearComplete) {
CONN_A.send(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_REQ);
}
[] CONN_B.receive(tr_BSSMAP_LclsNotificationSts(LCLS_STS_not_possible_ls));
}
f_sleep(2.0);
f_lcls_test_fini();
}
/* TODO:
* verify IP/Port information in LCLS-triggered MDCX
* establish with one side connect, then enable using LCLS CTRL
* LCLS CTRL for call that doesn't have LCLS enabled
* LCLS IEs without GCR in ASS CMD
* GCR updates?
* Handover related LCLS bits (after we have inter-BSC HO in OsmoBSC)
*/
control {
execute( TC_lcls_gcr_only() );
execute( TC_lcls_gcr_bway_connect() );
execute( TC_lcls_gcr_bway_connect_hr() );
execute( TC_lcls_gcr_bway_codec_mismatch() );
execute( TC_lcls_gcr_nomatch_bway_connect() );
execute( TC_lcls_gcr_bway_dont_connect() );
execute( TC_lcls_gcr_unsuppported_cfg() );
execute( TC_lcls_gcr_unsuppported_csc() );
execute( TC_lcls_gcr_bway_dont_connect_csc() );
execute( TC_lcls_connect_break() );
execute( TC_lcls_connect_clear() );
execute( TC_lcls_bts_gcr_bway_connect() );
execute( TC_lcls_bts_gcr_bway_connect_hr() );
execute( TC_lcls_bts_connect_break() );
}
}