stp: Refactor STP_Tests_IPA tests and introduce new TC_tmt_loadshare

Infrastructure in STP_Tests_IPA changed to be more similar to what is
done in STP_Tests_M3UA, which already contain more advanced tests.

Array of AS names mp_ipa_as_names is added in order to let TTCN3 port
which AS is configured in STP for each src port.

Change-Id: Iae213c58598cc0207503fd10f09d2d57aab941fe
diff --git a/stp/STP_Tests_IPA.ttcn b/stp/STP_Tests_IPA.ttcn
index 0a36921..f7273dc 100644
--- a/stp/STP_Tests_IPA.ttcn
+++ b/stp/STP_Tests_IPA.ttcn
@@ -33,11 +33,18 @@
 
 import from STP_Tests_Common all;
 
-private const integer NR_IPA := 1;
+private const integer NR_IPA := 4;
+
+type record of charstring AspNameArray;
 
 modulepar {
 	integer mp_stp_ipa_port := 5000;
 	integer mp_local_ipa_port := 20000;
+	AspNameArray mp_ipa_as_names := {"ipa-as-loadshare-sender",
+					 "ipa-as-loadshare-receiver",
+					 "ipa-as-loadshare-receiver",
+					 "ipa-as-dynamic-asp"
+					 };
 }
 
 type component IPA_CT extends Test_CT {
@@ -45,6 +52,7 @@
 	 * having to re-invent IPA CCM handling here */
 	port MTP3asp_PT IPA[NR_IPA];
 	var IPA_Emulation_CT vc_IPA[NR_IPA];
+	var IPA_CCM_Parameters g_ccm_pars[NR_IPA];
 }
 
 friend function f_IPA_send(integer idx, octetstring data) runs on IPA_CT {
@@ -53,7 +61,6 @@
 }
 
 friend function f_IPA_exp(integer idx, template (present) octetstring data) runs on IPA_CT {
-	var M3UA_RecvFrom rx;
 	alt {
 	[] IPA[idx].receive(t_ASP_MTP3_TRANSFERind(?, ?, ?, ?, data)) {
 		setverdict(pass);
@@ -65,6 +72,23 @@
 	}
 }
 
+private function f_rnd_ipa_len() runs on IPA_CT return integer {
+	var integer rnd_len := f_rnd_int(100);
+	/* We need at least 1 byte of data, othewise osmocom IPA stack will discard and close the socket */
+	if (rnd_len == 0) {
+		rnd_len := 1;
+	}
+	return rnd_len;
+}
+
+/* Test if traffic is routed from idx_tx to idx_rx */
+private function f_test_traffic(integer idx_tx, integer idx_rx)
+runs on IPA_CT {
+	var octetstring data := f_rnd_octstring(f_rnd_ipa_len());
+	f_IPA_send(idx_tx, data);
+	f_IPA_exp(idx_rx, data);
+}
+
 friend function f_init_ipa() runs on IPA_CT {
 	var integer i;
 
@@ -74,11 +98,15 @@
 		vc_IPA[i] := IPA_Emulation_CT.create("IPA" & int2str(i));
 		map(vc_IPA[i]:IPA_PORT, system:IPA_CODEC_PT);
 		connect(self:IPA[i], vc_IPA[i]:MTP3_SP_PORT);
-		vc_IPA[i].start(IPA_Emulation.main_client(mp_stp_ip, mp_stp_ipa_port, mp_local_ip,
-				mp_local_ipa_port+i));
+		g_ccm_pars[i] := c_IPA_default_ccm_pars;
+		g_ccm_pars[i].name := mp_ipa_as_names[i];
 	}
 }
 
+friend function f_connect_ipa(integer idx) runs on IPA_CT {
+	vc_IPA[idx].start(IPA_Emulation.main_client(mp_stp_ip, mp_stp_ipa_port, mp_local_ip,
+			mp_local_ipa_port+idx, g_ccm_pars[idx]));
+}
 
 
 /* "accept-asp-connections pre-configured" and client from unknown source */
@@ -87,6 +115,10 @@
 	f_vty_config2(VTY, {"cs7 instance 0", "listen ipa 5000"},
 		      "accept-asp-connections pre-configured");
 	f_init_ipa();
+	/* Add 100 to the port since we know that port is not configured in any
+	  ASP only up to NR_IPA are configured. */
+	vc_IPA[0].start(IPA_Emulation.main_client(mp_stp_ip, mp_stp_ipa_port, mp_local_ip,
+			mp_local_ipa_port+100, g_ccm_pars[0]));
 	f_sleep(1.0);
 	if (IPA[0].checkstate("Connected")) {
 		setverdict(fail, "Expected IPA port to be disconnected");
@@ -103,9 +135,8 @@
 	f_init_common();
 	f_vty_config2(VTY, {"cs7 instance 0", "listen ipa 5000"},
 		      "accept-asp-connections pre-configured");
-	f_vty_config2(VTY, {"cs7 instance 0"}, "asp ipa-mahlzeit0 20000 5000 ipa");
-	f_vty_config2(VTY, {"cs7 instance 0", "as mahlzeit ipa"}, "asp ipa-mahlzeit0");
 	f_init_ipa();
+	f_connect_ipa(0);
 	f_sleep(1.0);
 	if (not IPA[0].checkstate("Connected")) {
 		setverdict(fail, "Expected IPA port to be connected");
@@ -115,7 +146,6 @@
 	/* switch back to default */
 	f_vty_config2(VTY, {"cs7 instance 0", "listen ipa 5000"},
 		      "accept-asp-connections dynamic-permitted");
-	f_vty_config2(VTY, {"cs7 instance 0"}, "no asp ipa-mahlzeit0");
 }
 
 
@@ -123,6 +153,10 @@
 testcase TC_unknown_client_dynamic() runs on IPA_CT {
 	f_init_common();
 	f_init_ipa();
+	/* Add 100 to the port since we know that port is not configured in any
+	  ASP only up to NR_IPA are configured. */
+	vc_IPA[0].start(IPA_Emulation.main_client(mp_stp_ip, mp_stp_ipa_port, mp_local_ip,
+			mp_local_ipa_port+100, g_ccm_pars[0]));
 	f_sleep(1.0);
 	if (not IPA[0].checkstate("Connected")) {
 		setverdict(fail, "Expected IPA port to be connected");
@@ -131,11 +165,61 @@
 	}
 }
 
+private altstep as_count_rx(integer idx, template (present) octetstring exp, inout integer counter)
+runs on IPA_CT {
+	[] IPA[idx].receive(t_ASP_MTP3_TRANSFERind(?, ?, ?, ?, exp)) {
+		counter := counter + 1;
+		}
+}
+
+/* test "traffic-mode load-share" behavior */
+testcase TC_tmt_loadshare() runs on IPA_CT {
+	var integer i;
+
+	f_init_ipa();
+
+	/* bring up the 'sender' side (single ASP in AS) */
+	f_connect_ipa(0);
+	/* activate the first 'receiver' side ASP */
+	f_connect_ipa(1);
+	f_sleep(1.0);
+
+	/* verify traffic is routed from sender to [sole] receiver */
+	for (i := 0; i < 10; i := i+1) {
+		f_test_traffic(0, 1);
+	}
+
+	/* activate the second 'receiver' side ASP */
+	f_connect_ipa(2);
+	f_sleep(1.0);
+
+	/* verify traffic is routed from sender to new receiver */
+	const integer iter_per_asp := 5;
+	var integer num_rx[3] := { 0, 0, 0 };
+	for (i := 0; i < 2*iter_per_asp; i := i+1) {
+		var octetstring data := f_rnd_octstring(f_rnd_ipa_len());
+		f_IPA_send(0, data);
+		alt {
+		[] as_count_rx(1, data, num_rx[1])
+		[] as_count_rx(2, data, num_rx[2])
+		}
+	}
+	/* FIXME: check for extraneous messages? */
+	for (i := 1; i <= 2; i := i+1) {
+		if (num_rx[i] != iter_per_asp) {
+			setverdict(fail, "Received ", num_rx[i], " out of expected ", iter_per_asp,
+				   "DATA messages at IPA port ", i);
+		}
+	}
+	setverdict(pass);
+}
+
 
 control {
 	execute( TC_unknown_client_nodynamic() );
 	execute( TC_known_client_nodynamic() );
 	execute( TC_unknown_client_dynamic() );
+	execute( TC_tmt_loadshare() );
 }