bsc: add TC_assignment_codec_fr_by_mode_modify

Test the lchan mode modification code path of OsmoBSC, in preparation of
adding VAMOS bits to the mode modification procedure.

Related: SYS#4895
Change-Id: Idf4efaed986de0bbd2b663313e837352cc139f0f
diff --git a/bsc/BSC_Tests.ttcn b/bsc/BSC_Tests.ttcn
index bc243b1..b07e693 100644
--- a/bsc/BSC_Tests.ttcn
+++ b/bsc/BSC_Tests.ttcn
@@ -3612,36 +3612,39 @@
 
 	f_establish_fully(ass_cmd, exp_compl);
 
-	/* Verify that the RSL-side activation actually matches our expectations */
-	var RSL_Message rsl := f_rslem_get_last_act(RSL_PROC, 0, g_chan_nr);
+	if (not g_pars.expect_channel_mode_modify) {
+		/* Verify that the RSL-side activation actually matches our expectations */
+		var RSL_Message rsl := f_rslem_get_last_act(RSL_PROC, 0, g_chan_nr);
 
-	var RSL_IE_Body mode_ie;
-	if (f_rsl_find_ie(rsl, RSL_IE_CHAN_MODE, mode_ie) == false) {
-		setverdict(fail, "Couldn't find CHAN_MODE IE");
-		mtc.stop;
-	}
-	var template RSL_IE_Body t_mode_ie := f_rsl_chmod_tmpl_from_codec(g_pars.ass_codec_list.codecElements[0]);
-	if (not match(mode_ie, t_mode_ie)) {
-		setverdict(fail, "RSL Channel Mode IE doesn't match expectation");
-	}
-
-	var RSL_IE_Body mr_conf;
-	if (g_pars.expect_mr_conf_ie != omit) {
-		if (f_rsl_find_ie(rsl, RSL_IE_MR_CONFIG, mr_conf) == false) {
-			setverdict(fail, "Missing MR CONFIG IE in RSL Chan Activ");
+		var RSL_IE_Body mode_ie;
+		if (f_rsl_find_ie(rsl, RSL_IE_CHAN_MODE, mode_ie) == false) {
+			setverdict(fail, "Couldn't find CHAN_MODE IE");
 			mtc.stop;
 		}
-		log("found RSL MR CONFIG IE: ", mr_conf);
-
-		if (not match(mr_conf, g_pars.expect_mr_conf_ie)) {
-			setverdict(fail, "RSL MR CONFIG IE does not match expectation. Expected: ",
-				g_pars.expect_mr_conf_ie);
+		var template RSL_IE_Body t_mode_ie := f_rsl_chmod_tmpl_from_codec(g_pars.ass_codec_list.codecElements[0]);
+		if (not match(mode_ie, t_mode_ie)) {
+			log("mode_ie ", mode_ie, " != t_mode_ie ", t_mode_ie);
+			setverdict(fail, "RSL Channel Mode IE doesn't match expectation");
 		}
-	} else {
-		if (f_rsl_find_ie(rsl, RSL_IE_MR_CONFIG, mr_conf) == true) {
+
+		var RSL_IE_Body mr_conf;
+		if (g_pars.expect_mr_conf_ie != omit) {
+			if (f_rsl_find_ie(rsl, RSL_IE_MR_CONFIG, mr_conf) == false) {
+				setverdict(fail, "Missing MR CONFIG IE in RSL Chan Activ");
+				mtc.stop;
+			}
 			log("found RSL MR CONFIG IE: ", mr_conf);
-			setverdict(fail, "Found MR CONFIG IE in RSL Chan Activ, expecting omit");
-			mtc.stop;
+
+			if (not match(mr_conf, g_pars.expect_mr_conf_ie)) {
+				setverdict(fail, "RSL MR CONFIG IE does not match expectation. Expected: ",
+					g_pars.expect_mr_conf_ie);
+			}
+		} else {
+			if (f_rsl_find_ie(rsl, RSL_IE_MR_CONFIG, mr_conf) == true) {
+				log("found RSL MR CONFIG IE: ", mr_conf);
+				setverdict(fail, "Found MR CONFIG IE in RSL Chan Activ, expecting omit");
+				mtc.stop;
+			}
 		}
 	}
 }
@@ -3803,6 +3806,30 @@
 	f_shutdown_helper();
 }
 
+/* Establish signalling on a TCH/F lchan, and then switch to speech mode without a new Assignment. */
+testcase TC_assignment_codec_fr_by_mode_modify() runs on test_CT {
+	var TestHdlrParams pars := f_gen_test_hdlr_pars();
+	var MSC_ConnHdlr vc_conn;
+
+	f_init(1, true);
+	f_sleep(1.0);
+
+	/* By disabling all SDCCH, the MS should be given a TCH/F for signalling. Then activating an FR codec should
+	 * merely do a Channel Mode Modify, and not assign to a new lchan. f_establish_fully() already accounts for
+	 * expecting a Channel Mode Modify if the channel type is compatible. */
+	f_disable_all_sdcch();
+	f_disable_all_tch_h();
+
+	pars.ass_codec_list := valueof(ts_BSSMAP_IE_CodecList({ts_CodecFR}));
+	pars.expect_channel_mode_modify := true;
+	vc_conn := f_start_handler(refers(f_TC_assignment_codec), pars);
+	vc_conn.done;
+
+	f_enable_all_sdcch();
+	f_enable_all_tch();
+	f_shutdown_helper();
+}
+
 /* 'amr start-mode auto' should not keep the (unused) 'smod' bits from previous configuration */
 testcase TC_assignment_codec_amr_startmode_cruft() runs on test_CT {
 	var TestHdlrParams pars := f_gen_test_hdlr_pars();
@@ -4062,6 +4089,20 @@
 	f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 5 sub-slot 1 unused");
 }
 
+private function f_disable_all_sdcch() runs on test_CT {
+	f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 0 sub-slot 0 borken");
+	f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 0 sub-slot 1 borken");
+	f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 0 sub-slot 2 borken");
+	f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 0 sub-slot 3 borken");
+}
+
+private function f_enable_all_sdcch() runs on test_CT {
+	f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 0 sub-slot 0 unused");
+	f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 0 sub-slot 1 unused");
+	f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 0 sub-slot 2 unused");
+	f_vty_transceive(BSCVTY, "bts 0 trx 0 timeslot 0 sub-slot 3 unused");
+}
+
 /* Allow HR only */
 private function f_TC_assignment_codec_xr_exhausted_req_hr(charstring id) runs on MSC_ConnHdlr {
 	g_pars := f_gen_test_hdlr_pars();
@@ -8608,6 +8649,7 @@
 	execute( TC_ciph_mode_a5_3() );
 
 	execute( TC_assignment_codec_fr() );
+	execute( TC_assignment_codec_fr_by_mode_modify() );
 	execute( TC_assignment_codec_hr() );
 	execute( TC_assignment_codec_efr() );
 	execute( TC_assignment_codec_amr_f() );