summaryrefslogtreecommitdiff
path: root/pjsip-apps
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2008-06-20 00:25:55 +0000
committerBenny Prijono <bennylp@teluu.com>2008-06-20 00:25:55 +0000
commit8dd0b7bc51da96d550888aeb199cee06d0b1a002 (patch)
treee97afee7367bc360e1ec5e2e9303bbd899514348 /pjsip-apps
parentd43d82894549014370cb97b965c6e36862d1950d (diff)
More ticket #543: added mod_sendto.py to send arbitrary INVITE request
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2033 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip-apps')
-rw-r--r--pjsip-apps/src/test-pjsua/README.TXT32
-rw-r--r--pjsip-apps/src/test-pjsua/inc_cfg.py7
-rw-r--r--pjsip-apps/src/test-pjsua/inc_sdp.py38
-rw-r--r--pjsip-apps/src/test-pjsua/inc_sip.py226
-rw-r--r--pjsip-apps/src/test-pjsua/mod_sendto.py50
-rw-r--r--pjsip-apps/src/test-pjsua/run.py8
-rw-r--r--pjsip-apps/src/test-pjsua/runall.py10
-rw-r--r--pjsip-apps/src/test-pjsua/scripts-sendto/100_simplecall.py20
-rw-r--r--pjsip-apps/src/test-pjsua/scripts-sendto/200_ice_no_ice.py26
-rw-r--r--pjsip-apps/src/test-pjsua/scripts-sendto/200_ice_success_1.py31
-rw-r--r--pjsip-apps/src/test-pjsua/scripts-sendto/200_ice_success_2.py35
-rw-r--r--pjsip-apps/src/test-pjsua/scripts-sendto/201_ice_mismatch_1.py29
-rw-r--r--pjsip-apps/src/test-pjsua/scripts-sendto/201_ice_mismatch_2.py31
-rw-r--r--pjsip-apps/src/test-pjsua/scripts-sendto/201_ice_mismatch_3.py30
14 files changed, 557 insertions, 16 deletions
diff --git a/pjsip-apps/src/test-pjsua/README.TXT b/pjsip-apps/src/test-pjsua/README.TXT
index fed4be79..f21c7908 100644
--- a/pjsip-apps/src/test-pjsua/README.TXT
+++ b/pjsip-apps/src/test-pjsua/README.TXT
@@ -4,7 +4,8 @@
0. What is this
---------------
-This is the automated testing scripts for pjsua. It can do many things (just don't ask it to write good documentation :) ).
+This is the automated testing scripts for pjsua. It can do many things (just don't ask
+it to write good documentation :) ).
1. Requirements
@@ -13,8 +14,7 @@ To run the tests you need:
- Python (tested with Python 2.5.2)
- pjsua application, built and placed in pjsip-apps/bin directory
- the pjsua must be built with:
- - TLS enabled
- - SRTP enabled
+ - SRTP enabled (the default)
2. Using
@@ -25,10 +25,30 @@ To run all the tests:
To run individual test:
$ python run.py MODULE CONFIG
-The run.py is the main entry for the test. It imports the various inc_xxx.py files, and it will load the MODULE. The MODULE contains specific test flows, and we have couple of them:
- - mod_run.py: this module implements a simple test which just run pjsua with the configuration from CONFIG file and checks if pjsua can start properly.
- - mod_call.py: this module implements call testing where it spawns two pjsua instances each with configurations as specified in CONFIG file, makes one pjsua call the other, and checks if the call can be established.
+The run.py is the main entry for the test. It imports the various inc_xxx.py files, and
+it will load the MODULE. The MODULE contains specific test flows, and we have few of
+them:
+
+ - mod_run.py:
+ a simple test which just run pjsua with the configuration from CONFIG file
+ and checks if pjsua can start properly.
+
+ - mod_call.py:
+ call testing where it spawns two pjsua instances each with configurations as
+ specified in CONFIG file, makes one pjsua call the other, and checks if the call
+ can be established.
+
+ - mod_pres.py:
+ presence testing
+
+ - mod_sendto.py:
+ Simple UAC to send arbitrary SIP message
+
+ - mod_media_playrec.py:
+ Media testing
Example:
$ python run.py mod_run.py scripts-run/100_simple.py
$ python run.py mod_call.py scripts-call/100_simple.py
+
+
diff --git a/pjsip-apps/src/test-pjsua/inc_cfg.py b/pjsip-apps/src/test-pjsua/inc_cfg.py
index d8519c3f..88b169c5 100644
--- a/pjsip-apps/src/test-pjsua/inc_cfg.py
+++ b/pjsip-apps/src/test-pjsua/inc_cfg.py
@@ -86,4 +86,11 @@ class TestParam:
self.user_data = user_data
+###################################
+# TestError exception
+class TestError:
+ desc = ""
+ def __init__(self, desc):
+ self.desc = desc
+
diff --git a/pjsip-apps/src/test-pjsua/inc_sdp.py b/pjsip-apps/src/test-pjsua/inc_sdp.py
new file mode 100644
index 00000000..4a0672d7
--- /dev/null
+++ b/pjsip-apps/src/test-pjsua/inc_sdp.py
@@ -0,0 +1,38 @@
+# $Id:$
+
+# SDP template
+sdp_templ = \
+"""v=0\r
+o=- 1 1 $NET_TYPE $ADDR_TYPE $LOCAL_IP\r
+s=pjmedia\r
+t=0 0\r
+$SDP_LINES"""
+
+sdp_media_templ = \
+"""m=$MEDIA_TYPE $PORT $TRANSPORT 0\r
+c=$NET_TYPE $ADDR_TYPE $LOCAL_IP\r
+$SDP_LINES"""
+
+# Create SDP session
+def session(local_ip="127.0.0.1", extra_lines="", net_type="IN", addr_type="IP4"):
+ sdp = sdp_templ
+ sdp = sdp.replace("$NET_TYPE", net_type)
+ sdp = sdp.replace("$ADDR_TYPE", addr_type)
+ sdp = sdp.replace("$LOCAL_IP", local_ip)
+ sdp = sdp.replace("$SDP_LINES", extra_lines)
+ return sdp
+
+# Create basic SDP media
+def media(media_type="audio", local_port=4000, local_ip="127.0.0.1", extra_lines="",
+ net_type = "IN", addr_type="IP4", transport="RTP/AVP"):
+ sdp = sdp_media_templ
+ sdp = sdp.replace("$MEDIA_TYPE", media_type)
+ sdp = sdp.replace("$LOCAL_IP", local_ip)
+ sdp = sdp.replace("$PORT", str(local_port))
+ sdp = sdp.replace("$NET_TYPE", net_type)
+ sdp = sdp.replace("$ADDR_TYPE", addr_type)
+ sdp = sdp.replace("$TRANSPORT", transport)
+ sdp = sdp.replace("$SDP_LINES", extra_lines)
+ return sdp
+
+
diff --git a/pjsip-apps/src/test-pjsua/inc_sip.py b/pjsip-apps/src/test-pjsua/inc_sip.py
new file mode 100644
index 00000000..04b5a96e
--- /dev/null
+++ b/pjsip-apps/src/test-pjsua/inc_sip.py
@@ -0,0 +1,226 @@
+# $Id:$
+#
+from socket import *
+import re
+import random
+import time
+import sys
+import inc_cfg as cfg
+from select import *
+
+# SIP request template
+req_templ = \
+"""$METHOD $TARGET_URI SIP/2.0\r
+Via: SIP/2.0/UDP $LOCAL_IP:$LOCAL_PORT;rport;branch=z9hG4bK$BRANCH\r
+Max-Forwards: 70\r
+From: <sip:caller@pjsip.org>$FROM_TAG\r
+To: <$TARGET_URI>$TO_TAG\r
+Contact: <sip:$LOCAL_IP:$LOCAL_PORT;transport=udp>\r
+Call-ID: $CALL_ID@pjsip.org\r
+CSeq: $CSEQ $METHOD\r
+Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, REFER\r
+Supported: replaces, 100rel, norefersub\r
+User-Agent: pjsip.org Python tester\r
+Content-Length: $CONTENT_LENGTH\r
+$SIP_HEADERS"""
+
+
+def is_request(msg):
+ return msg.split(" ", 1)[0] != "SIP/2.0"
+
+def is_response(msg):
+ return msg.split(" ", 1)[0] == "SIP/2.0"
+
+def get_code(msg):
+ if msg=="":
+ return 0
+ return int(msg.split(" ", 2)[1])
+
+def get_tag(msg, hdr="To"):
+ pat = "^" + hdr + ":.*"
+ result = re.search(pat, msg, re.M | re.I)
+ if result==None:
+ return ""
+ line = result.group()
+ #print "line=", line
+ tags = line.split(";tag=")
+ if len(tags)>1:
+ return tags[1]
+ return ""
+ #return re.split("[;& ]", s)
+
+
+class Dialog:
+ sock = None
+ dst_addr = ""
+ dst_port = 5060
+ local_ip = ""
+ local_port = 0
+ tcp = False
+ call_id = str(random.random())
+ cseq = 0
+ local_tag = ";tag=" + str(random.random())
+ rem_tag = ""
+ last_resp_code = 0
+ inv_branch = ""
+ trace_enabled = True
+ last_request = ""
+ def __init__(self, dst_addr, dst_port=5060, tcp=False, trace=True):
+ self.dst_addr = dst_addr
+ self.dst_port = dst_port
+ self.tcp = tcp
+ self.trace_enabled = trace
+ if tcp==True:
+ self.sock = socket(AF_INET, SOCK_STREAM)
+ self.sock.connect(dst_addr, dst_port)
+ else:
+ self.sock = socket(AF_INET, SOCK_DGRAM)
+ self.sock.bind(("127.0.0.1", 0))
+
+ self.local_ip, self.local_port = self.sock.getsockname()
+ self.trace("Dialog socket bound to " + self.local_ip + ":" + str(self.local_port))
+
+ def trace(self, txt):
+ if self.trace_enabled:
+ print str(time.strftime("%H:%M:%S ")) + txt
+
+ def create_req(self, method, sdp, branch="", extra_headers=""):
+ if branch=="":
+ self.cseq = self.cseq + 1
+ msg = req_templ
+ msg = msg.replace("$METHOD", method)
+ if self.tcp:
+ transport_param = ";transport=tcp"
+ else:
+ transport_param = ""
+ msg = msg.replace("$TARGET_URI", "sip:"+self.dst_addr+":"+str(self.dst_port) + transport_param)
+ msg = msg.replace("$LOCAL_IP", self.local_ip)
+ msg = msg.replace("$LOCAL_PORT", str(self.local_port))
+ if branch=="":
+ branch=str(random.random())
+ msg = msg.replace("$BRANCH", branch)
+ msg = msg.replace("$FROM_TAG", self.local_tag)
+ msg = msg.replace("$TO_TAG", self.rem_tag)
+ msg = msg.replace("$CALL_ID", self.call_id)
+ msg = msg.replace("$CSEQ", str(self.cseq))
+ msg = msg.replace("$SIP_HEADERS", extra_headers)
+ if sdp!="":
+ msg = msg.replace("$CONTENT_LENGTH", str(len(sdp)))
+ msg = msg + "Content-Type: application/sdp\r\n"
+ else:
+ msg = msg.replace("$CONTENT_LENGTH", "0")
+ msg = msg + "\r\n"
+ msg = msg + sdp
+ return msg
+
+ def create_invite(self, sdp, extra_headers=""):
+ self.inv_branch = str(random.random())
+ return self.create_req("INVITE", sdp, branch=self.inv_branch, extra_headers=extra_headers)
+
+ def create_ack(self, sdp="", extra_headers=""):
+ return self.create_req("ACK", sdp, extra_headers=extra_headers, branch=self.inv_branch)
+
+ def create_bye(self, extra_headers=""):
+ return self.create_req("BYE", "", extra_headers)
+
+ def send_msg(self, msg):
+ if (is_request(msg)):
+ self.last_request = msg.split(" ", 1)[0]
+ self.trace("============== TX MSG ============= \n" + msg)
+ self.sock.sendto(msg, 0, (self.dst_addr, self.dst_port))
+
+ def wait_msg(self, timeout):
+ endtime = time.time() + timeout
+ msg = ""
+ while time.time() < endtime:
+ readset = select([self.sock], [], [], timeout)
+ if len(readset) < 1 or not self.sock in readset[0]:
+ if len(readset) < 1:
+ print "select() returns " + str(len(readset))
+ elif not self.sock in readset[0]:
+ print "select() alien socket"
+ else:
+ print "select other error"
+ continue
+ try:
+ msg = self.sock.recv(2048)
+ except:
+ print "recv() exception: ", sys.exc_info()[0]
+ continue
+
+ if msg=="":
+ return ""
+ if self.last_request=="INVITE" and self.rem_tag=="":
+ self.rem_tag = get_tag(msg, "To")
+ self.rem_tag = self.rem_tag.rstrip("\r\n;")
+ if self.rem_tag != "":
+ self.rem_tag = ";tag=" + self.rem_tag
+ self.trace("=== rem_tag:" + self.rem_tag)
+ self.trace("=========== RX MSG ===========\n" + msg)
+ return msg
+
+ # Send request and wait for final response
+ def send_request_wait(self, msg, timeout):
+ t1 = 1.0
+ endtime = time.time() + timeout
+ resp = ""
+ code = 0
+ for i in range(0,5):
+ self.send_msg(msg)
+ resp = self.wait_msg(t1)
+ if resp!="" and is_response(resp):
+ code = get_code(resp)
+ break
+ last_resp = resp
+ while code < 200 and time.time() < endtime:
+ resp = self.wait_msg(endtime - time.time())
+ if resp != "" and is_response(resp):
+ code = get_code(resp)
+ last_resp = resp
+ elif resp=="":
+ break
+ return last_resp
+
+ def hangup(self, last_code=0):
+ self.trace("====== hangup =====")
+ if last_code!=0:
+ self.last_resp_code = last_code
+ if self.last_resp_code>0 and self.last_resp_code<200:
+ msg = self.create_req("CANCEL", "", branch=self.inv_branch, extra_headers="")
+ self.send_request_wait(msg, 5)
+ msg = self.create_ack()
+ self.send_msg(msg)
+ elif self.last_resp_code>=200 and self.last_resp_code<300:
+ msg = self.create_ack()
+ self.send_msg(msg)
+ msg = self.create_bye()
+ self.send_request_wait(msg, 5)
+ else:
+ msg = self.create_ack()
+ self.send_msg(msg)
+
+
+class SendtoCfg:
+ # Test name
+ name = ""
+ # pjsua InstanceParam
+ inst_param = None
+ # Initial SDP
+ sdp = ""
+ # Expected code
+ resp_code = 0
+ # Use TCP?
+ use_tcp = False
+ # List of RE patterns that must exist in response
+ resp_include = []
+ # List of RE patterns that must NOT exist in response
+ resp_exclude = []
+ # Constructor
+ def __init__(self, name, pjsua_args, sdp, resp_code, resp_inc=[], resp_exc=[], use_tcp=False):
+ self.sdp = sdp
+ self.resp_code = resp_code
+ self.resp_include = resp_inc
+ self.resp_exclude = resp_exc
+ self.use_tcp = use_tcp
+ self.inst_param = cfg.InstanceParam("pjsua", pjsua_args)
+
diff --git a/pjsip-apps/src/test-pjsua/mod_sendto.py b/pjsip-apps/src/test-pjsua/mod_sendto.py
new file mode 100644
index 00000000..92437e97
--- /dev/null
+++ b/pjsip-apps/src/test-pjsua/mod_sendto.py
@@ -0,0 +1,50 @@
+# $Id:$
+import imp
+import sys
+import inc_sip as sip
+import inc_const as const
+import re
+from inc_cfg import *
+
+# Read configuration
+cfg_file = imp.load_source("cfg_file", sys.argv[2])
+
+# Test body function
+def test_func(t, userdata):
+ pjsua = t.process[0]
+ # Create dialog
+ dlg = sip.Dialog("127.0.0.1", pjsua.inst_param.sip_port,
+ tcp=cfg_file.sendto_cfg.use_tcp)
+ #dlg = sip.Dialog("127.0.0.1", 5060, tcp=cfg_file.sendto_cfg.use_tcp)
+ cfg = cfg_file.sendto_cfg
+
+ req = dlg.create_invite(cfg.sdp)
+ resp = dlg.send_request_wait(req, 10)
+ if resp=="":
+ raise TestError("Timed-out waiting for response")
+ # Check response code
+ code = int(sip.get_code(resp))
+ if code != cfg.resp_code:
+ dlg.hangup(code)
+ raise TestError("Expecting code " + str(cfg.resp_code) +
+ " got " + str(code))
+ # Check for patterns that must exist
+ for p in cfg.resp_include:
+ if re.search(p, resp, re.M | re.I)==None:
+ dlg.hangup(code)
+ raise TestError("Pattern " + p + " not found")
+ # Check for patterns that must not exist
+ for p in cfg.resp_exclude:
+ if re.search(p, resp, re.M | re.I)!=None:
+ dlg.hangup(code)
+ raise TestError("Excluded pattern " + p + " found")
+ pjsua.sync_stdout()
+ dlg.hangup(code)
+ pjsua.sync_stdout()
+
+# Here where it all comes together
+test = TestParam(cfg_file.sendto_cfg.name,
+ [cfg_file.sendto_cfg.inst_param],
+ test_func)
+
+
diff --git a/pjsip-apps/src/test-pjsua/run.py b/pjsip-apps/src/test-pjsua/run.py
index 34792dad..74c028a9 100644
--- a/pjsip-apps/src/test-pjsua/run.py
+++ b/pjsip-apps/src/test-pjsua/run.py
@@ -8,6 +8,7 @@ import random
import time
import inc_const as const
+from inc_cfg import *
# Get the pjsua executable name
if sys.platform.find("win32")!=-1:
@@ -43,13 +44,6 @@ else:
G_EXE = G_EXE.rstrip("\n\r \t")
###################################
-# TestError exception
-class TestError:
- desc = ""
- def __init__(self, desc):
- self.desc = desc
-
-###################################
# Poor man's 'expect'-like class
class Expect:
proc = None
diff --git a/pjsip-apps/src/test-pjsua/runall.py b/pjsip-apps/src/test-pjsua/runall.py
index dc1119da..11ea5927 100644
--- a/pjsip-apps/src/test-pjsua/runall.py
+++ b/pjsip-apps/src/test-pjsua/runall.py
@@ -12,9 +12,9 @@ tests = []
# Excluded tests (because they fail?)
excluded_tests = [ "svn",
"pyc",
- "scripts-call/150_srtp_1_2",
- "scripts-call/150_srtp_2_1",
- "scripts-call/300_ice_1_1"]
+ #"scripts-call/150_srtp_1_2",
+ "scripts-call/150_srtp_2_1"
+ ]
# Add basic tests
for f in os.listdir("scripts-run"):
@@ -28,6 +28,10 @@ for f in os.listdir("scripts-call"):
for f in os.listdir("scripts-pres"):
tests.append("mod_pres.py scripts-pres/" + f)
+# Add mod_sendto tests
+for f in os.listdir("scripts-sendto"):
+ tests.append("mod_sendto.py scripts-sendto/" + f)
+
# Filter-out excluded tests
for pat in excluded_tests:
tests = [t for t in tests if t.find(pat)==-1]
diff --git a/pjsip-apps/src/test-pjsua/scripts-sendto/100_simplecall.py b/pjsip-apps/src/test-pjsua/scripts-sendto/100_simplecall.py
new file mode 100644
index 00000000..748b2b43
--- /dev/null
+++ b/pjsip-apps/src/test-pjsua/scripts-sendto/100_simplecall.py
@@ -0,0 +1,20 @@
+# $Id:$
+import inc_sip as sip
+import inc_sdp as sdp
+
+sdp = \
+"""
+v=0
+o=- 0 0 IN IP4 127.0.0.1
+s=pjmedia
+c=IN IP4 127.0.0.1
+t=0 0
+m=audio 4000 RTP/AVP 0 101
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+"""
+
+sendto_cfg = sip.SendtoCfg( "simple call", "--null-audio", sdp, 200)
+
diff --git a/pjsip-apps/src/test-pjsua/scripts-sendto/200_ice_no_ice.py b/pjsip-apps/src/test-pjsua/scripts-sendto/200_ice_no_ice.py
new file mode 100644
index 00000000..8f8d1041
--- /dev/null
+++ b/pjsip-apps/src/test-pjsua/scripts-sendto/200_ice_no_ice.py
@@ -0,0 +1,26 @@
+# $Id:$
+import inc_sip as sip
+import inc_sdp as sdp
+
+sdp = \
+"""
+v=0
+o=- 0 0 IN IP4 127.0.0.1
+s=pjmedia
+c=IN IP4 127.0.0.1
+t=0 0
+m=audio 4000 RTP/AVP 0 101
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+"""
+
+args = "--null-audio --use-ice --auto-answer 200 --max-calls 1"
+include = []
+exclude = ["a=ice", "a=candidate"]
+
+sendto_cfg = sip.SendtoCfg( "caller has no ice, answer must not have ICE",
+ pjsua_args=args, sdp=sdp, resp_code=200,
+ resp_inc=include, resp_exc=exclude)
+
diff --git a/pjsip-apps/src/test-pjsua/scripts-sendto/200_ice_success_1.py b/pjsip-apps/src/test-pjsua/scripts-sendto/200_ice_success_1.py
new file mode 100644
index 00000000..a4680b72
--- /dev/null
+++ b/pjsip-apps/src/test-pjsua/scripts-sendto/200_ice_success_1.py
@@ -0,0 +1,31 @@
+# $Id:$
+import inc_sip as sip
+import inc_sdp as sdp
+
+sdp = \
+"""
+v=0
+o=- 0 0 IN IP4 127.0.0.1
+s=pjmedia
+c=IN IP4 127.0.0.1
+t=0 0
+m=audio 4000 RTP/AVP 0 101
+a=ice-ufrag:1234
+a=ice-pwd:5678
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+a=candidate:XX 1 UDP 1234 127.0.0.1 4000 typ host
+"""
+
+args = "--null-audio --use-ice --auto-answer 200 --max-calls 1"
+include = ["a=ice-ufrag"] # must have ICE
+exclude = ["a=candidate:[0-9a-zA-Z]+ 2 UDP", # must not answer with 2 components
+ "ice-mismatch" # must not mismatch
+ ]
+
+sendto_cfg = sip.SendtoCfg( "caller sends only one component",
+ pjsua_args=args, sdp=sdp, resp_code=200,
+ resp_inc=include, resp_exc=exclude)
+
diff --git a/pjsip-apps/src/test-pjsua/scripts-sendto/200_ice_success_2.py b/pjsip-apps/src/test-pjsua/scripts-sendto/200_ice_success_2.py
new file mode 100644
index 00000000..00e747d8
--- /dev/null
+++ b/pjsip-apps/src/test-pjsua/scripts-sendto/200_ice_success_2.py
@@ -0,0 +1,35 @@
+# $Id:$
+import inc_sip as sip
+import inc_sdp as sdp
+
+sdp = \
+"""
+v=0
+o=- 0 0 IN IP4 127.0.0.1
+s=pjmedia
+c=IN IP4 127.0.0.1
+t=0 0
+m=audio 4000 RTP/AVP 0 101
+a=rtcp:4382 IN IP4 192.168.0.4
+a=ice-ufrag:1234
+a=ice-pwd:5678
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+a=candidate:XX 1 UDP 1234 127.0.0.1 4000 typ host
+a=candidate:YY 2 UDP 1234 192.168.0.4 4382 typ host
+"""
+
+args = "--null-audio --use-ice --auto-answer 200 --max-calls 1"
+include = ["a=ice-ufrag", # must have ICE
+ "a=candidate:[0-9a-zA-Z]+ 2 UDP" # must have RTCP component
+ ]
+exclude = [
+ "ice-mismatch" # must not mismatch
+ ]
+
+sendto_cfg = sip.SendtoCfg( "caller sends only one component",
+ pjsua_args=args, sdp=sdp, resp_code=200,
+ resp_inc=include, resp_exc=exclude)
+
diff --git a/pjsip-apps/src/test-pjsua/scripts-sendto/201_ice_mismatch_1.py b/pjsip-apps/src/test-pjsua/scripts-sendto/201_ice_mismatch_1.py
new file mode 100644
index 00000000..e0cb563e
--- /dev/null
+++ b/pjsip-apps/src/test-pjsua/scripts-sendto/201_ice_mismatch_1.py
@@ -0,0 +1,29 @@
+# $Id:$
+import inc_sip as sip
+import inc_sdp as sdp
+
+sdp = \
+"""
+v=0
+o=- 0 0 IN IP4 127.0.0.1
+s=pjmedia
+c=IN IP4 127.0.0.1
+t=0 0
+m=audio 4000 RTP/AVP 0 101
+a=ice-ufrag:1234
+a=ice-pwd:5678
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+a=candidate:XX 1 UDP 1 1.1.1.1 2222 typ host
+"""
+
+args = "--null-audio --use-ice --auto-answer 200 --max-calls 1"
+include = ["a=ice-mismatch"]
+exclude = []
+
+sendto_cfg = sip.SendtoCfg( "caller sends mismatched offer for comp 1",
+ pjsua_args=args, sdp=sdp, resp_code=200,
+ resp_inc=include, resp_exc=exclude)
+
diff --git a/pjsip-apps/src/test-pjsua/scripts-sendto/201_ice_mismatch_2.py b/pjsip-apps/src/test-pjsua/scripts-sendto/201_ice_mismatch_2.py
new file mode 100644
index 00000000..869891ee
--- /dev/null
+++ b/pjsip-apps/src/test-pjsua/scripts-sendto/201_ice_mismatch_2.py
@@ -0,0 +1,31 @@
+# $Id:$
+import inc_sip as sip
+import inc_sdp as sdp
+
+sdp = \
+"""
+v=0
+o=- 0 0 IN IP4 127.0.0.1
+s=pjmedia
+c=IN IP4 127.0.0.1
+t=0 0
+m=audio 4000 RTP/AVP 0 101
+a=rtcp:4382 IN IP4 192.168.0.4
+a=ice-ufrag:1234
+a=ice-pwd:5678
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+a=candidate:XX 1 UDP 1234 127.0.0.1 4000 typ host
+a=candidate:XX 2 UDP 1234 127.0.0.1 4000 typ host
+"""
+
+args = "--null-audio --use-ice --auto-answer 200 --max-calls 1"
+include = ["a=ice-mismatch"]
+exclude = []
+
+sendto_cfg = sip.SendtoCfg( "caller sends mismatched offer for comp 2",
+ pjsua_args=args, sdp=sdp, resp_code=200,
+ resp_inc=include, resp_exc=exclude)
+
diff --git a/pjsip-apps/src/test-pjsua/scripts-sendto/201_ice_mismatch_3.py b/pjsip-apps/src/test-pjsua/scripts-sendto/201_ice_mismatch_3.py
new file mode 100644
index 00000000..8a8b0d80
--- /dev/null
+++ b/pjsip-apps/src/test-pjsua/scripts-sendto/201_ice_mismatch_3.py
@@ -0,0 +1,30 @@
+# $Id:$
+import inc_sip as sip
+import inc_sdp as sdp
+
+sdp = \
+"""
+v=0
+o=- 0 0 IN IP4 127.0.0.1
+s=pjmedia
+c=IN IP4 127.0.0.1
+t=0 0
+m=audio 4000 RTP/AVP 0 101
+a=rtcp:4382 IN IP4 192.168.0.4
+a=ice-ufrag:1234
+a=ice-pwd:5678
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+a=candidate:XX 1 UDP 1234 127.0.0.1 4000 typ host
+"""
+
+args = "--null-audio --use-ice --auto-answer 200 --max-calls 1"
+include = ["a=ice-mismatch"]
+exclude = []
+
+sendto_cfg = sip.SendtoCfg( "caller sends mismatched offer for comp 2",
+ pjsua_args=args, sdp=sdp, resp_code=200,
+ resp_inc=include, resp_exc=exclude)
+