dexter has uploaded this change for review. ( https://gerrit.osmocom.org/9768


Change subject: MGCP_Test: add tests to verify actual RTP flows
......................................................................

MGCP_Test: add tests to verify actual RTP flows

The test coverage of the RTP aspects of the MGW is currently very
minima. Lets add a few more testcase to verify RTP behaves as
expected in various situations.

- Add testcase TC_one_crcx_receive_only_rtp:
  Test recvonly mode of the MGW. All packets must be absorbed by
  the MGW, no packets must come back.

- Add testcase TC_one_crcx_loopback_rtp:
  Test loopback mode of the MGW. All packet sent to the MGW must
  come back.

- Add testcase TC_two_crcx_and_rtp_bidir:
  We already test unidirectional transmissions. This test does
  the same as TC_two_crcx_and_rtp but for both directions.

- Add testcase TC_two_crcx_mdcx_and_rtp:
  Simulate a typical behaviour of a normal call. First create
  two half open connections and complete the connections later
  using MDCX.

- Add testcase TC_two_crcx_and_unsolicited_rtp:
  Test what happens when a RTP packets from rogue source are mixed
  into the RTP stream.

- Add testcase TC_two_crcx_and_one_mdcx_rtp_ho:
  Test a typical handover situation. An existing connection is
  handovered to another source on one end but the old source will
  keep transmitting for a while.

Change-Id: I556a6efff0e74aab897bd8165200eec36e46629f
Closes: OS#2703
---
M library/RTP_Emulation.ttcn
M mgw/MGCP_Test.ttcn
M mgw/expected-results.xml
3 files changed, 423 insertions(+), 33 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks 
refs/changes/68/9768/1

diff --git a/library/RTP_Emulation.ttcn b/library/RTP_Emulation.ttcn
index 20e4299..475b478 100644
--- a/library/RTP_Emulation.ttcn
+++ b/library/RTP_Emulation.ttcn
@@ -188,19 +188,56 @@
        return stats;
 }

-function f_rtpem_stats_compare(RtpemStats a, RtpemStats b) return boolean {
-       log("stats A: ", a);
-       log("stats B: ", b);
+function f_rtpem_stats_compare_value(integer a, integer b, integer tolerance 
:= 0) return boolean {
+       var integer temp;

-       if (a.num_pkts_tx != b.num_pkts_rx or
-           a.num_pkts_rx != b.num_pkts_tx or
-           a.bytes_payload_tx != b.bytes_payload_rx or
-           a.bytes_payload_rx != b.bytes_payload_tx) {
+       temp := (a - b)
+       if (temp < 0) {
+               temp := -temp;
+       }
+
+       if (temp > tolerance) {
                return false;
        }
+
        return true;
 }

+/* Cross-compare two rtpem-statistics. The transmission statistics on the a 
side
+ * must match the reception statistics on the other side and vice versa. The
+ * user may also supply a tolerance value (number of packets) when deviations
+ * are acceptable */
+function f_rtpem_stats_compare(RtpemStats a, RtpemStats b, integer tolerance 
:= 0) return boolean {
+       var integer plen;
+
+       log("stats A: ", a);
+       log("stats B: ", b);
+       log("tolerance: ", tolerance, " packets");
+
+       if (f_rtpem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx, 
tolerance) == false) {
+               return false;
+       }
+
+       if (f_rtpem_stats_compare_value(a.num_pkts_rx, b.num_pkts_tx, 
tolerance) == false) {
+               return false;
+       }
+
+       if(a.num_pkts_tx > 0) {
+               plen := a.bytes_payload_tx / a.num_pkts_tx;
+       } else {
+               plen := 0;
+       }
+
+       if (f_rtpem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx, 
tolerance * plen) == false) {
+               return false;
+       }
+
+       if (f_rtpem_stats_compare_value(a.bytes_payload_rx, b.bytes_payload_tx, 
tolerance * plen) == false) {
+               return false;
+       }
+
+       return true;
+}

 template PDU_RTP ts_RTP(BIT32_BO_LAST ssrc, INT7b pt, LIN2_BO_LAST seq, 
uint32_t ts,
                        octetstring payload, BIT1 marker := '0'B) := {
diff --git a/mgw/MGCP_Test.ttcn b/mgw/MGCP_Test.ttcn
index 6868405..8746c38 100644
--- a/mgw/MGCP_Test.ttcn
+++ b/mgw/MGCP_Test.ttcn
@@ -22,8 +22,8 @@
                var ConnectionId g_mgcp_conn_id := -1;
                var integer g_trans_id;

-               var RTP_Emulation_CT vc_RTPEM[2];
-               port RTPEM_CTRL_PT RTPEM[2];
+               var RTP_Emulation_CT vc_RTPEM[3];
+               port RTPEM_CTRL_PT RTPEM[3];
        };

        function get_next_trans_id() runs on dummy_CT return MgcpTransId {
@@ -246,10 +246,10 @@
                MgcpConnectionId mgcp_conn_id optional
        }

-       function f_flow_create(RTPEM_CTRL_PT pt, MgcpEndpoint ep, inout 
RtpFlowData flow,
+       /* Create an RTP flow (bidirectional, or receive-only) */
+       function f_flow_create(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId 
call_id, charstring mode, inout RtpFlowData flow,
                                boolean one_phase := true)
        runs on dummy_CT {
-               var MgcpCallId call_id := '1226'H;
                var template MgcpCommand cmd;
                var MgcpResponse resp;

@@ -257,8 +257,12 @@
                f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);

                if (one_phase) {
-                       /* Connect flow to MGW */
-                       cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", 
call_id);
+                       /* Connect flow to MGW using a CRCX that also contains 
an SDP
+                        * part that tells the MGW where we are listening for 
RTP streams
+                        * that come from the MGW. We get a fully working 
connection in
+                        * one go. */
+
+                       cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
                        cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, 
"23", "42",
                                          flow.em.portnr, { int2str(flow.pt) },
                                          { valueof(ts_SDP_rtpmap(flow.pt, 
flow.codec)),
@@ -269,27 +273,65 @@
                        flow.mgw.portnr :=
                                
resp.sdp.media_list[0].media_field.ports.port_number;
                } else {
-                       /* first create the MGW side RTP socket */
-                       cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", 
call_id);
+                       /* Create a half-open connection only. We do not tell 
the MGW
+                        * where it can send RTP streams to us. This means this
+                        * connection will only be able to receive but can not 
send
+                        * data back to us. In order to turn the connection in 
a fully
+                        * bi-directional one, a separate MDCX is needed. */
+
+                       cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
                        resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
                        flow.mgcp_conn_id := extract_conn_id(resp);
                        /* extract MGW-side port number from response */
                        flow.mgw.portnr :=
                                
resp.sdp.media_list[0].media_field.ports.port_number;
-
-                       /* then connect it to the emulation-side RTP socket 
using SDP */
-                       cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", 
call_id, flow.mgcp_conn_id);
-                       cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, 
"23", "42",
-                                         flow.em.portnr, { int2str(flow.pt) },
-                                         { valueof(ts_SDP_rtpmap(flow.pt, 
flow.codec)),
-                                           valueof(ts_SDP_ptime(20)) });
-                       resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
-
                }
                /* finally, connect the emulation-side RTP socket to the MGW */
                f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
        }

+       /* Modify an existing RTP flow */
+       function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId 
call_id, charstring mode, inout RtpFlowData flow)
+       runs on dummy_CT {
+               var template MgcpCommand cmd;
+               var MgcpResponse resp;
+
+               /* rebind local RTP emulation socket to the new address */
+               f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
+
+               /* connect MGW side RTP socket to the emulation-side RTP socket 
using SDP */
+               cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, 
flow.mgcp_conn_id);
+               cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", 
"42",
+                                 flow.em.portnr, { int2str(flow.pt) },
+                                 { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)),
+                                   valueof(ts_SDP_ptime(20)) });
+               resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
+
+               /* extract MGW-side port number from response. (usually this
+                * will not change, but thats is up to the MGW) */
+               flow.mgw.portnr :=
+                       resp.sdp.media_list[0].media_field.ports.port_number;
+
+               /* reconnect the emulation-side RTP socket to the MGW */
+               f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
+       }
+
+       /* Delete an existing RTP flow */
+       function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := 
omit, template MgcpCallId call_id := omit)
+       runs on dummy_CT {
+               var template MgcpCommand cmd;
+               var MgcpResponse resp;
+
+               /* Switch off RTP flow */
+               f_rtpem_mode(pt, RTPEM_MODE_NONE);
+
+               /* Delete connection on MGW (if needed) */
+               if (isvalue(call_id) and isvalue(ep)) {
+                       f_sleep(0.1);
+                       f_dlcx_ok(valueof(ep), call_id);
+               }
+       }
+
        function f_crcx(charstring ep_prefix) runs on dummy_CT {
                var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
                var template MgcpCommand cmd;
@@ -862,14 +904,92 @@
                setverdict(pass);
        }

-       /* create two local RTP emulations; create two connections on MGW EP, 
exchange some data */
-       testcase TC_two_crcx_and_rtp() runs on dummy_CT {
+       /* Create one half open connection in receive-only mode. The MGW must 
accept
+        * the packets but must not send any. */
+       testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
+               var RtpFlowData flow;
+               var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
+               var MgcpCallId call_id := '1225'H;
+               var RtpemStats stats;
+
+               f_init(ep);
+               flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, 
"AMR/8000/1"));
+               flow.em.portnr := 10000;
+               f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, false);
+
+               f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
+               f_sleep(1.0);
+               f_flow_delete(RTPEM[0], ep, call_id);
+
+               stats := f_rtpem_stats_get(RTPEM[0]);
+
+               if (stats.num_pkts_tx < 40) {
+                       setverdict(fail);
+               }
+               if (stats.bytes_payload_tx < 190) {
+                       setverdict(fail);
+               }
+               if (stats.num_pkts_rx != 0) {
+                       setverdict(fail);
+               }
+               if (stats.num_pkts_rx_err_seq != 0) {
+                       setverdict(fail);
+               }
+               if (stats.num_pkts_rx_err_ts != 0) {
+                       setverdict(fail);
+               }
+               if (stats.num_pkts_rx_err_disabled != 0) {
+                       setverdict(fail);
+               }
+
+               setverdict(pass);
+       }
+
+       /* Create one connection in loopback mode, test if the RTP packets are
+        * actually reflected */
+       testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
+               var RtpFlowData flow;
+               var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
+               var MgcpCallId call_id := '1225'H;
+               var RtpemStats stats;
+
+               f_init(ep);
+               flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, 
"GSM-HR-08/8000/1"));
+               flow.em.portnr := 10000;
+               f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
+
+               f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
+               f_sleep(1.0);
+               f_flow_delete(RTPEM[0], ep, call_id);
+
+               stats := f_rtpem_stats_get(RTPEM[0]);
+
+               if (stats.num_pkts_tx != stats.num_pkts_rx) {
+                       setverdict(fail);
+               }
+               if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
+                       setverdict(fail);
+               }
+               if (stats.num_pkts_rx_err_seq != 0) {
+                       setverdict(fail);
+               }
+               if (stats.num_pkts_rx_err_ts != 0) {
+                       setverdict(fail);
+               }
+               if (stats.num_pkts_rx_err_disabled != 0) {
+                       setverdict(fail);
+               }
+
+               setverdict(pass);
+       }
+
+       function f_TC_two_crcx_and_rtp(boolean bidir) runs on dummy_CT {
                var RtpFlowData flow[2];
                var RtpemStats stats[2];
-               var template MgcpCommand cmd;
                var MgcpResponse resp;
                var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
                var MgcpCallId call_id := '1226'H;
+               var integer tolerance := 0;

                f_init(ep);

@@ -877,20 +997,181 @@
                flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, 
"AMR/8000"));
                /* bind local RTP emulation sockets */
                flow[0].em.portnr := 10000;
-               f_flow_create(RTPEM[0], ep, flow[0]);
+               f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);

                /* from MGW back to us */
                flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, 
"AMR/8000"));
                flow[1].em.portnr := 20000;
-               f_flow_create(RTPEM[1], ep, flow[1]);
+               f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
+
+               if (bidir) {
+                               f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
+                               f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
+
+                               /* Note: When we test bidirectional we may
+                                * loose packets during switch off because
+                                * both ends are transmitting and we only
+                                * can switch them off one by one. */
+                               tolerance := 3;
+               } else {
+                               f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
+                               f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
+               }
+
+               f_sleep(1.0);
+
+               f_flow_delete(RTPEM[1]);
+               f_flow_delete(RTPEM[0], ep, call_id);
+
+               stats[0] := f_rtpem_stats_get(RTPEM[0]);
+               stats[1] := f_rtpem_stats_get(RTPEM[1]);
+               if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
+                       setverdict(fail, "RTP endpoint statistics don't match");
+               }
+
+               setverdict(pass);
+       }
+
+       /* create two local RTP emulations; create two connections on MGW EP, 
exchange some data */
+       testcase TC_two_crcx_and_rtp() runs on dummy_CT {
+                f_TC_two_crcx_and_rtp(false);
+       }
+
+       /* create two local RTP emulations; create two connections on MGW EP,
+        * exchange some data in both directions */
+       testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
+                f_TC_two_crcx_and_rtp(true);
+       }
+
+       /* create two local RTP emulations and pass data in both directions */
+       testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
+               var RtpFlowData flow[2];
+               var RtpemStats stats[2];
+               var MgcpResponse resp;
+               var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
+               var MgcpCallId call_id := '1227'H;
+               var integer num_pkts_tx[2];
+               var integer temp;
+
+               f_init(ep);
+
+               /* Create the first connection in receive only mode */
+               flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, 
"GSM/8000/1"));
+               flow[0].em.portnr := 10000;
+               f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], 
false);
+
+               /* Create the second connection. This connection will be also
+                * in receive only mode */
+               flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, 
"GSM/8000/1"));
+               flow[1].em.portnr := 20000;
+               f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], 
false);
+
+               /* The first leg starts transmitting */
+               f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
+               f_sleep(0.5);
+               stats[0] := f_rtpem_stats_get(RTPEM[0]);
+               if (stats[0].num_pkts_rx_err_disabled != 0) {
+                       setverdict(fail, "received packets from MGW on recvonly 
connection");
+               }
+               stats[1] := f_rtpem_stats_get(RTPEM[1]);
+               if (stats[1].num_pkts_rx_err_disabled != 0) {
+                       setverdict(fail, "received packets from MGW on recvonly 
connection");
+               }
+
+               /* The second leg starts transmitting a little later */
+               f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
+               f_sleep(1.0);
+               stats[0] := f_rtpem_stats_get(RTPEM[0]);
+               if (stats[0].num_pkts_rx_err_disabled != 0) {
+                       setverdict(fail, "received packets from MGW on recvonly 
connection");
+               }
+               stats[1] := f_rtpem_stats_get(RTPEM[1]);
+               if (stats[1].num_pkts_rx_err_disabled != 0) {
+                       setverdict(fail, "received packets from MGW on recvonly 
connection");
+               }
+
+               /* The first leg will now be switched into bidirectional
+                * mode, but we do not expect any data comming back yet. */
+               f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
+               f_sleep(0.5);
+               stats[0] := f_rtpem_stats_get(RTPEM[0]);
+               if (stats[1].num_pkts_rx_err_disabled != 0) {
+                       setverdict(fail, "received packets from MGW on recvonly 
connection");
+               }
+               stats[1] := f_rtpem_stats_get(RTPEM[1]);
+               if (stats[1].num_pkts_rx_err_disabled != 0) {
+                       setverdict(fail, "received packets from MGW on recvonly 
connection");
+               }
+
+               /* When the second leg is switched into bidirectional mode
+                * as well, then the MGW will connect the two together and
+                * we should see RTP streams passing through from both ends. */
+               f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
+               f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
+               stats[0] := f_rtpem_stats_get(RTPEM[0]);
+               num_pkts_tx[0] := stats[0].num_pkts_tx
+               stats[1] := f_rtpem_stats_get(RTPEM[1]);
+               num_pkts_tx[1] := stats[1].num_pkts_tx
+
+               f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
+               f_sleep(2.0);
+
+               stats[0] := f_rtpem_stats_get(RTPEM[0]);
+               stats[1] := f_rtpem_stats_get(RTPEM[1]);
+
+               temp := stats[0].num_pkts_tx - num_pkts_tx[0] - 
stats[1].num_pkts_rx;
+               if (temp > 3 or temp < -3) {
+                       setverdict(fail, "number of packets not within normal 
parameters");
+               }
+
+               temp := stats[1].num_pkts_tx - num_pkts_tx[1] - 
stats[0].num_pkts_rx;
+               if (temp > 3 or temp < -3) {
+                       setverdict(fail, "number of packets not within normal 
parameters");
+               }
+
+               /* Tear down */
+               f_flow_delete(RTPEM[0]);
+               f_flow_delete(RTPEM[1], ep, call_id);
+               setverdict(pass);
+       }
+
+       /* Test what happens when two RTP streams from different sources target
+        * a single connection. Is the unsolicited stream properly ignored? */
+       testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
+               var RtpFlowData flow[2];
+               var RtpemStats stats[2];
+               var MgcpResponse resp;
+               var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
+               var MgcpCallId call_id := '1234321326'H;
+               var integer unsolicited_port := 10002;
+
+               f_init(ep);
+
+               /* from us to MGW */
+               flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, 
"AMR/8000"));
+               /* bind local RTP emulation sockets */
+               flow[0].em.portnr := 10000;
+               f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
+
+               /* from MGW back to us */
+               flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, 
"AMR/8000"));
+               flow[1].em.portnr := 20000;
+               f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);

                f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
                f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);

-               f_sleep(1.0);
+               f_sleep(0.5);

-               f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
-               f_sleep(0.1);
+               /* Start inserting unsolicited RTP packets */
+               f_rtpem_bind(RTPEM[2], "127.0.0.1", unsolicited_port);
+               f_rtpem_connect(RTPEM[2], "127.0.0.1", flow[0].mgw.portnr);
+               f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
+
+               f_sleep(0.5);
+
+               f_flow_delete(RTPEM[0]);
+               f_flow_delete(RTPEM[1], ep, call_id);

                stats[0] := f_rtpem_stats_get(RTPEM[0]);
                stats[1] := f_rtpem_stats_get(RTPEM[1]);
@@ -898,9 +1179,68 @@
                        setverdict(fail, "RTP endpoint statistics don't match");
                }

-               f_dlcx_ok(ep, call_id);
                setverdict(pass);
+       }

+       /* Test a handover situation. We first create two connections transmit
+        * some data bidirectionally. Then we will simulate a handover 
situation. */
+       testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
+               var RtpFlowData flow[2];
+               var RtpemStats stats[3];
+               var MgcpResponse resp;
+               var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
+               var MgcpCallId call_id := '76338'H;
+               var integer port_old;
+
+               f_init(ep);
+
+               /* First connection (BTS) */
+               flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, 
"GSM-EFR/8000"));
+               /* bind local RTP emulation sockets */
+               flow[0].em.portnr := 10000;
+               f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
+
+               /* Second connection (PBX) */
+               flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, 
"GSM-EFR/8000"));
+               flow[1].em.portnr := 20000;
+               f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
+
+               /* Normal rtp flow for one second */
+               f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
+               f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
+               f_sleep(1.0);
+
+               /* Now switch the flow over to a new port (BTS) */
+               port_old := flow[0].em.portnr;
+               flow[0].em.portnr := 10002;
+               f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
+
+               /* When handing over a call, the old source may still keep
+                * transmitting for a while. We simulate this by injecting
+                * some unsolicited packets on the behalf of the old source,
+                * (old remote port) */
+               f_rtpem_bind(RTPEM[2], "127.0.0.1", port_old);
+               f_rtpem_connect(RTPEM[2], "127.0.0.1", flow[0].mgw.portnr);
+               f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
+               f_sleep(1.0);
+               f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
+               f_sleep(1.0);
+
+               /* Terminate call */
+               f_flow_delete(RTPEM[0]);
+               f_flow_delete(RTPEM[1], ep, call_id);
+
+               stats[0] := f_rtpem_stats_get(RTPEM[0]);
+               stats[1] := f_rtpem_stats_get(RTPEM[1]);
+               if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
+                       setverdict(fail, "RTP endpoint statistics don't match");
+               }
+               stats[2] := f_rtpem_stats_get(RTPEM[2]);
+               if (stats[2].num_pkts_rx_err_disabled != 0) {
+                       setverdict(fail, "received packets on old leg after 
handover");
+               }
+
+               setverdict(pass);
        }

        /* TODO: Double-DLCX (no retransmission) */
@@ -943,6 +1283,13 @@
                execute(TC_crcx_dlcx_30ep());

                execute(TC_rtpem_selftest());
+
+               execute(TC_one_crcx_receive_only_rtp());
+               execute(TC_one_crcx_loopback_rtp());
                execute(TC_two_crcx_and_rtp());
+               execute(TC_two_crcx_and_rtp_bidir());
+               execute(TC_two_crcx_mdcx_and_rtp());
+               execute(TC_two_crcx_and_unsolicited_rtp());
+               execute(TC_two_crcx_and_one_mdcx_rtp_ho());
        }
 }
diff --git a/mgw/expected-results.xml b/mgw/expected-results.xml
index 03c8fd2..f201099 100644
--- a/mgw/expected-results.xml
+++ b/mgw/expected-results.xml
@@ -31,4 +31,10 @@
   <testcase classname='MGCP_Test' name='TC_crcx_dlcx_30ep' time='MASKED'/>
   <testcase classname='MGCP_Test' name='TC_rtpem_selftest' time='MASKED'/>
   <testcase classname='MGCP_Test' name='TC_two_crcx_and_rtp' time='MASKED'/>
+  <testcase classname='MGCP_Test' name='TC_one_crcx_receive_only_rtp' 
time='MASKED'/>
+  <testcase classname='MGCP_Test' name='TC_one_crcx_loopback_rtp' 
time='MASKED'/>
+  <testcase classname='MGCP_Test' name='TC_two_crcx_and_rtp_bidir' 
time='MASKED'/>
+  <testcase classname='MGCP_Test' name='TC_two_crcx_mdcx_and_rtp' 
time='MASKED'/>
+  <testcase classname='MGCP_Test' name='TC_two_crcx_and_unsolicited_rtp' 
time='MASKED'/>
+  <testcase classname='MGCP_Test' name='TC_two_crcx_and_one_mdcx_rtp_ho' 
time='MASKED'/>
 </testsuite>

--
To view, visit https://gerrit.osmocom.org/9768
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: I556a6efff0e74aab897bd8165200eec36e46629f
Gerrit-Change-Number: 9768
Gerrit-PatchSet: 1
Gerrit-Owner: dexter <[email protected]>

Reply via email to