bsc: TC_ho_in_fail_mgw_mdcx_timeout: new test

Verify that OsmoBSC runs into a timeout and aborts the handover if the
MDCX is not answered by the MGW. This test ensures it still works after
removing a redundant timer, as discussed here:
https://gerrit.osmocom.org/c/osmo-bsc/+/30307/1#message-9e02be4de78c03ed25b8422d6eee4ba9c6dda9ff

Related: OS#5787
Change-Id: I30e1811f97406cff6ba794fcd6882e2bb0205087
diff --git a/bsc/BSC_Tests.ttcn b/bsc/BSC_Tests.ttcn
index 439b234..96428ff 100644
--- a/bsc/BSC_Tests.ttcn
+++ b/bsc/BSC_Tests.ttcn
@@ -6344,6 +6344,12 @@
 		}
 	}
 
+	if (g_pars.expect_ho_fail_lchan_est) {
+		BSSAP.receive(tr_BSSMAP_HandoverFailure);
+		setverdict(pass);
+		return;
+	}
+
 	/* The RSL Emulation magically accepts the Chan Activ behind the scenes. */
 
 	/* we're sure that the channel activation is done now, verify the parameters in it */
@@ -6460,6 +6466,9 @@
 	f_ho_into_this_bsc(id, oldToNewBSSIEs);
 	if (g_pars.expect_ho_fail) {
 		f_perform_clear_no_lchan();
+	} else if (g_pars.expect_ho_fail_lchan_est) {
+		BSSAP.receive(tr_BSSMAP_ClearRequest);
+		f_perform_clear_no_lchan();
 	} else {
 		f_perform_clear(exp_rr_rel_tmpl := exp_rr_rel_tmpl);
 	}
@@ -6485,6 +6494,9 @@
 	if (pars.expect_ho_fail) {
 		f_ctrs_bsc_and_bts_add(0, "handover:failed");
 		f_ctrs_bsc_and_bts_add(0, "interbsc_ho_in:failed");
+	} else if (pars.expect_ho_fail_lchan_est) {
+		f_ctrs_bsc_and_bts_add(0, "handover:error");
+		f_ctrs_bsc_and_bts_add(0, "interbsc_ho_in:error");
 	} else {
 		f_ctrs_bsc_and_bts_add(0, "handover:completed");
 		f_ctrs_bsc_and_bts_add(0, "interbsc_ho_in:completed");
@@ -7029,6 +7041,16 @@
 	f_shutdown_helper(ho := true);
 }
 
+testcase TC_ho_in_fail_mgw_mdcx_timeout() runs on test_CT {
+	var TestHdlrParams pars := f_gen_test_hdlr_pars();
+
+	pars.ignore_mgw_mdcx := true;
+	pars.expect_ho_fail_lchan_est := true;
+
+	f_tc_ho_into_this_bsc_main(pars);
+	f_shutdown_helper(ho := true);
+}
+
 /* An incoming inter-BSC HO can either issue the Handover Request message attached to the initial SCCP N-Connect (as in
  * the other tests we have so far), or the first CR can be "empty" with the BSSAP request following later. Test the
  * empty N-Connect case. */
@@ -12334,6 +12356,7 @@
 	execute( TC_ho_in_fail_msc_clears_after_ho_detect() );
 	execute( TC_ho_in_fail_no_detect() );
 	execute( TC_ho_in_fail_no_detect2() );
+	execute( TC_ho_in_fail_mgw_mdcx_timeout() );
 	execute( TC_ho_into_this_bsc_sccp_cr_without_bssap() );
 
 	execute( TC_ho_neighbor_config_1() );
diff --git a/bsc/MSC_ConnectionHandler.ttcn b/bsc/MSC_ConnectionHandler.ttcn
index 13fe723..ed9c13b 100644
--- a/bsc/MSC_ConnectionHandler.ttcn
+++ b/bsc/MSC_ConnectionHandler.ttcn
@@ -476,7 +476,7 @@
 		}
 	}
 
-	[g_pars.aoip] MGCP.receive(tr_MDCX) -> value mgcp_cmd {
+	[g_pars.aoip and not g_pars.ignore_mgw_mdcx] MGCP.receive(tr_MDCX) -> value mgcp_cmd {
 		mgcp_resp := f_rx_mdcx(mgcp_cmd);
 		MGCP.send(mgcp_resp);
 		if(norepeat == false) {
@@ -484,7 +484,8 @@
 		}
 	}
 
-	[not g_pars.aoip] MGCP_MULTI.receive(tr_MGCP_RecvFrom_any(msg_mdcx)) -> value mrf {
+	[not g_pars.aoip and not g_pars.ignore_mgw_mdcx]
+			MGCP_MULTI.receive(tr_MGCP_RecvFrom_any(msg_mdcx)) -> value mrf {
 		mgcp_resp := f_rx_mdcx(mrf.msg.command);
 		msg_resp := {
 			response := mgcp_resp
@@ -756,7 +757,9 @@
 	uint3_t		expect_tsc optional,
 	BSSMAP_IE_CellIdentifier	cell_id_source,
 	boolean		expect_ho_fail,
-	boolean		inter_bsc_ho_in__ho_req_in_initial_sccp_cr
+	boolean		expect_ho_fail_lchan_est,
+	boolean		inter_bsc_ho_in__ho_req_in_initial_sccp_cr,
+	boolean		ignore_mgw_mdcx
 };
 
 /* Note: Do not use valueof() to get a value of this template, use
@@ -801,7 +804,9 @@
 	expect_tsc := omit,
 	cell_id_source := valueof(ts_CellID_LAC_CI(1, 1)),
 	expect_ho_fail := false,
-	inter_bsc_ho_in__ho_req_in_initial_sccp_cr := true
+	expect_ho_fail_lchan_est := false,
+	inter_bsc_ho_in__ho_req_in_initial_sccp_cr := true,
+	ignore_mgw_mdcx := false
 }
 
 function f_create_chan_and_exp(template (present) PDU_BSSAP exp_l3_compl := ?)