#9621: Implement conference functional tests

This commit is contained in:
Alexandre Savard
2012-04-15 10:27:51 -04:00
parent 00126a3611
commit ced6c682d2
5 changed files with 408 additions and 94 deletions

View File

@ -59,11 +59,15 @@ class SflPhoneCtrl(Thread):
onCallCurrent_cb
onCallBusy_cb
onCallFailure_cb
onConferenceCreated_cb
"""
# list of active calls (known by the client)
activeCalls = {}
# list of active conferences
activeConferences = {}
def __init__(self, name=sys.argv[0]):
Thread.__init__(self)
@ -74,6 +78,7 @@ class SflPhoneCtrl(Thread):
self.name = name
self.currentCallId = ""
self.currentConfId = ""
self.isStop = False
@ -145,6 +150,7 @@ class SflPhoneCtrl(Thread):
print "Adding Incoming call method"
proxy_callmgr.connect_to_signal('incomingCall', self.onIncomingCall)
proxy_callmgr.connect_to_signal('callStateChanged', self.onCallStateChanged)
proxy_callmgr.connect_to_signal('conferenceCreated', self.onConferenceCreated)
except dbus.DBusException, e:
print e
@ -171,7 +177,7 @@ class SflPhoneCtrl(Thread):
def onIncomingCall_cb(self):
pass
def onCallHangup_cb(self):
def onCallHangup_cb(self, callId):
pass
def onCallRinging_cb(self):
@ -202,7 +208,7 @@ class SflPhoneCtrl(Thread):
def onCallHangUp(self, callid):
""" Remove callid from call list """
self.onCallHangup_cb()
self.onCallHangup_cb(callid)
self.currentCallId = ""
del self.activeCalls[callid]
@ -274,6 +280,12 @@ class SflPhoneCtrl(Thread):
else:
print "unknown state"
def onConferenceCreated_cb(self):
pass
def onConferenceCreated(self, confId):
self.currentConfId = confId
self.onConferenceCreated_cb()
#
# Account management
@ -545,9 +557,6 @@ class SflPhoneCtrl(Thread):
for call in self.activeCalls:
print "\t" + call
#
# Action
#
def Call(self, dest):
"""Start a call and return a CallID
@ -636,12 +645,6 @@ class SflPhoneCtrl(Thread):
def Hold(self, callid):
"""Hold a call identified by a CallID"""
# if not self.account:
# self.setFirstRegisteredAccount()
# if not self.isAccountRegistered():
# raise SflPhoneError("Can't hold a call without a registered account")
if callid is None or callid == "":
raise SflPhoneError("Invalid callID")
@ -651,12 +654,6 @@ class SflPhoneCtrl(Thread):
def UnHold(self, callid):
"""Unhold an incoming call identified by a CallID"""
# if not self.account:
# self.setFirstRegisteredAccount()
# if not self.isAccountRegistered():
# raise SflPhoneError("Can't unhold a call without a registered account")
if callid is None or callid == "":
raise SflPhoneError("Invalid callID")
@ -679,13 +676,30 @@ class SflPhoneCtrl(Thread):
callid = m.hexdigest()
return callid
def createConference(self, call1Id, call2Id):
""" Create a conference given the two call ids """
self.callmanager.joinParticipant(call1Id, call2Id)
def hangupConference(self, confId):
""" Hang up each call for this conference """
self.callmanager.hangUpConference(confId)
def run(self):
"""Processing method for this thread"""
context = self.loop.get_context()
context = self.loop.get_context()
while True:
context.iteration(True)
if self.isStop:
print "++++++++++++++++++++++++++++++++++++++++"
print "++++++++++++++++++++++++++++++++++++++++"
print "++++++++++++++++++++++++++++++++++++++++"
print "++++++++++++++++++++++++++++++++++++++++"
return

View File

@ -1,6 +1,8 @@
#!/usr/bin/env python
#
# Copyright (C) 2009 by the Free Software Foundation, Inc.
# Copyright (C) 2012 by the Free Software Foundation, Inc.
#
# Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@ -22,7 +24,7 @@ class SippWrapper:
""" Wrapper taht allow for managing sipp command line easily """
def __init__(self):
self.commandLine = "sipp"
self.commandLine = "./sipp"
self.remoteServer = ""
self.remotePort = ""
self.localInterface = ""
@ -41,9 +43,11 @@ class SippWrapper:
self.enableTraceRtt = False
self.enableTraceLogs = False
def buildCommandLine(self):
def buildCommandLine(self, port):
""" Fill the command line arguments based on specified parameters """
self.localPort = str(port)
if not self.remotePort and not self.remoteServer:
self.isUserAgentClient = False
elif self.remotePort and not self.remoteServer:
@ -106,9 +110,8 @@ class SippWrapper:
def launch(self):
""" Launch the sipp instance using the specified arguments """
self.buildCommandLine()
print self.commandLine
return os.system(self.commandLine)
return os.system(self.commandLine + " 2>&1 > /dev/null")
class SippScreenStatParser:

View File

@ -1,6 +1,8 @@
#!/usr/bin/env python
#!/USR/BIN/ENV PYTHON
#
# Copyright (C) 2009 by the Free Software Foundation, Inc.
# Copyright (C) 2012 by the Free Software Foundation, Inc.
#
# Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@ -18,7 +20,9 @@
import os
import time
import yaml
import logging
import multiprocessing
from sippwrap import SippWrapper
from sippwrap import SippScreenStatParser
from sflphonectrl import SflPhoneCtrl
@ -29,10 +33,62 @@ from nose.tools import nottest
### function starting with 'test' are executed.
###
accountList = ["IP2IP", "Account:1332798167"]
SCENARIO_PATH = "../sippxml/"
class SippCtrl:
def __init__(self):
self.remoteServer = "127.0.0.1"
self.remotePort = str(5062)
self.localInterface = "127.0.0.1"
self.localPort = str(5060)
def initialize_sipp_registration_instance(self, instance, xmlScenario):
instance.remoteServer = self.remoteServer
instance.remotePort = self.remotePort
instance.localInterface = self.localInterface
instance.localPort = self.localPort
instance.customScenarioFile = SCENARIO_PATH + xmlScenario
instance.numberOfCall = 1
instance.numberOfSimultaneousCall = 1
def initialize_sipp_call_instance(self, instance):
instance.localInterface = self.localInterface
instance.localPort = self.localPort
instance.numberOfCall = 1
instance.numberOfSimultaneousCall = 1
instance.enableTraceScreen = True
def launchSippProcess(self, sippInstance, localPort):
sippInstance.buildCommandLine(localPort)
sippInstance.launch()
def find_sipp_pid(self):
# Retreive the PID of the last
# The /proc/PID/cmdline contain the command line from
pids = [int(x) for x in os.listdir("/proc") if x.isdigit()]
sippPid = [pid for pid in pids if "sipp" in open("/proc/" + str(pid) + "/cmdline").readline()]
return sippPid[0]
def clean_log_directory(self):
dirlist = os.listdir("./")
files = [x for x in dirlist if "screen.log" in x]
for f in files:
os.remove(f)
def parse_results(self):
dirlist = os.listdir("./")
logfile = [x for x in dirlist if "screen.log" in x]
fullpath = os.path.dirname(os.path.realpath(__file__)) + "/"
# there should be only one screen.log file (see clean_log_directory)
resultParser = SippScreenStatParser(fullpath + logfile[0])
assert(not resultParser.isAnyFailedCall())
assert(resultParser.isAnySuccessfulCall())
class TestSFLPhoneAccountConfig(SflPhoneCtrl):
""" The test suite for account configuration """
@ -46,14 +102,40 @@ class TestSFLPhoneAccountConfig(SflPhoneCtrl):
self.logger.addHandler(filehdlr)
self.logger.setLevel(logging.INFO)
@nottest
def get_config(self):
""" Parsse configuration file and return a dictionary """
config = {}
with open("sflphoned.functest.yml","r") as stream:
config = yaml.load(stream)
return config
def get_account_list_from_config(self):
""" Get the accout list from config and add IP2IP """
config = self.get_config()
accounts = config["preferences"]["order"]
accountList = accounts.split('/')
del accountList[len(accountList)-1]
accountList.append("IP2IP")
return accountList
def test_get_account_list(self):
self.logger.info("Test get account list")
accountList = self.get_account_list_from_config()
# make sure that the intersection between the list is of same size
accList = self.getAllAccounts()
listIntersection = set(accList) & set(accountList)
assert len(listIntersection) == len(accountList)
@nottest
def test_account_registration(self):
self.logger.info("Test account registration")
accList = [x for x in self.getAllAccounts() if x != "IP2IP"]
@ -72,6 +154,24 @@ class TestSFLPhoneAccountConfig(SflPhoneCtrl):
assert self.isAccountRegistered(acc)
@nottest
def test_get_account_details(self):
self.logger.info("Test account details")
accList = [x for x in self.getAllAccounts() if x != "IP2IP"]
config = self.get_config()
accountDetails = {}
for acc in accList:
accountDetails[acc] = self.getAccountDetails(acc)
accountConfDetails = {}
for accConf in config["accounts"]:
accountConfDetails[accConf["id"]] = accConf
@nottest
def test_add_remove_account(self):
self.logger.info("Test add/remove account")
@ -99,11 +199,12 @@ class TestSFLPhoneAccountConfig(SflPhoneCtrl):
class TestSFLPhoneRegisteredCalls(SflPhoneCtrl):
class TestSFLPhoneRegisteredCalls(SflPhoneCtrl, SippCtrl):
""" The test suite for call interaction """
def __init__(self):
SflPhoneCtrl.__init__(self)
SippCtrl.__init__(self)
self.logger = logging.getLogger("TestSFLPhoneRegisteredCalls")
filehdlr = logging.FileHandler("/tmp/sfltestregisteredcall.log")
@ -113,8 +214,6 @@ class TestSFLPhoneRegisteredCalls(SflPhoneCtrl):
self.logger.setLevel(logging.INFO)
self.sippRegistrationInstance = SippWrapper()
self.sippCallInstance = SippWrapper()
self.localInterface = "127.0.0.1"
self.localPort = str(5064)
# Make sure the test directory is populated with most recent log files
self.clean_log_directory()
@ -143,70 +242,26 @@ class TestSFLPhoneRegisteredCalls(SflPhoneCtrl):
self.stopThread()
def clean_log_directory(self):
dirlist = os.listdir("./")
files = [x for x in dirlist if "screen.log" in x]
for f in files:
os.remove(f)
def find_sipp_pid(self):
# Retreive the PID of the last
# The /proc/PID/cmdline contain the command line from
pids = [int(x) for x in os.listdir("/proc") if x.isdigit()]
sippPid = [pid for pid in pids if "sipp" in open("/proc/" + str(pid) + "/cmdline").readline()]
return sippPid[0]
def parse_results(self):
dirlist = os.listdir("./")
logfile = [x for x in dirlist if "screen.log" in x]
print logfile
fullpath = os.path.dirname(os.path.realpath(__file__)) + "/"
# there should be only one screen.log file (see clean_log_directory)
resultParser = SippScreenStatParser(fullpath + logfile[0])
assert(not resultParser.isAnyFailedCall())
assert(resultParser.isAnySuccessfulCall())
def test_registered_call(self):
self.logger.info("Test Registered Call")
# launch the sipp instance in background
# sipp 127.0.0.1:5060 -sf uac_register_no_cvs.xml -i 127.0.0.1 -p 5062
self.sippRegistrationInstance.remoteServer = "127.0.0.1"
self.sippRegistrationInstance.remotePort = str(5062)
self.sippRegistrationInstance.localInterface = self.localInterface
self.sippRegistrationInstance.localPort = self.localPort
self.sippRegistrationInstance.customScenarioFile = SCENARIO_PATH + "uac_register_no_cvs.xml"
self.sippRegistrationInstance.launchInBackground = True
self.sippRegistrationInstance.numberOfCall = 1
self.sippRegistrationInstance.numberOfSimultaneousCall = 1
# Launch a sipp instance for account registration on asterisk
# this account will then be used to receive call from sflphone
self.initialize_sipp_registration_instance(self.sippRegistrationInstance, "uac_register_no_cvs_300.xml")
regd = multiprocessing.Process(name='sipp1register', target=self.launchSippProcess,
args=(self.sippRegistrationInstance, 5064,))
regd.start()
self.sippRegistrationInstance.launch()
# wait for the registration to complete
regd.join()
# wait for this instance of sipp to complete registration
sippPid = self.find_sipp_pid()
while os.path.exists("/proc/" + str(sippPid)):
time.sleep(1)
# Launch a sipp instance waiting for a call from previously registered account
self.initialize_sipp_call_instance(self.sippCallInstance)
calld = multiprocessing.Process(name='sipp1call', target=self.launchSippProcess,
args=(self.sippCallInstance, 5064,))
calld.start()
# sipp -sn uas -p 5062 -i 127.0.0.1
self.sippCallInstance.localInterface = self.localInterface
self.sippCallInstance.localPort = self.localPort
self.sippCallInstance.launchInBackground = True
self.sippCallInstance.numberOfCall = 1
self.sippCallInstance.numberOfSimultaneousCall = 1
self.sippCallInstance.enableTraceScreen = True
self.sippCallInstance.launch()
sippPid = self.find_sipp_pid()
# make sure every account are enabled
# Make sure every account are enabled
accList = [x for x in self.getAllAccounts() if x != "IP2IP"]
for acc in accList:
if not self.isAccountRegistered(acc):
@ -215,11 +270,143 @@ class TestSFLPhoneRegisteredCalls(SflPhoneCtrl):
# Make a call to the SIPP instance
self.Call("300")
# Start Glib mainloop to process callbacks
# Start the threaded loop to handle GLIB cllbacks
self.start()
# Wait the sipp instance to dump log files
while os.path.exists("/proc/" + str(sippPid)):
time.sleep(1)
# Wait for the sipp instance to dump log files
calld.join()
self.stopThread()
self.parse_results()
class TestSFLPhoneConferenceCalls(SflPhoneCtrl, SippCtrl):
""" Test Conference calls """
def __init__(self):
SflPhoneCtrl.__init__(self)
SippCtrl.__init__(self)
self.logger = logging.getLogger("TestSFLPhoneRegisteredCalls")
filehdlr = logging.FileHandler("/tmp/sfltestregisteredcall.log")
formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
filehdlr.setFormatter(formatter)
self.logger.addHandler(filehdlr)
self.logger.setLevel(logging.INFO)
self.sippRegistrationInstanceA = SippWrapper()
self.sippRegistrationInstanceB = SippWrapper()
self.sippCallInstanceA = SippWrapper()
self.sippCallInstanceB = SippWrapper()
self.localPortCallA = str(5064)
self.localPortCallB = str(5066)
self.callCount = 0
self.accountCalls = []
# Make sure the test directory is populated with most recent log files
# self.clean_log_directory()
def onCallCurrent_cb(self):
""" On incoming call, answer the call, then hangup """
self.callCount += 1
self.accountCalls.append(self.currentCallId)
print "Account List: ", str(self.accountCalls)
if self.callCount == 2:
self.createConference(self.accountCalls[0], self.accountCalls[1])
def onCallRinging_cb(self):
""" Display messages when call is ringing """
print "The call is ringing"
def onCallHangup_cb(self, callId):
""" Exit thread when all call are finished """
if callId in self.accountCalls:
self.accountCalls.remove(callId)
self.callCount -= 1
if self.callCount == 0:
self.stopThread()
def onCallFailure_cb(self):
""" If a failure occurs duing the call, just leave the running thread """
print "Stopping Thread"
self.stopThread()
def onConferenceCreated_cb(self):
""" Called once the conference is created """
print "Conference Created ", self.currentConfId
print "Conference Hangup ", self.currentConfId
self.hangupConference(self.currentConfId)
def test_conference_call(self):
self.logger.info("Test Registered Call")
# launch the sipp instance to register the first participant to astersik
self.initialize_sipp_registration_instance(self.sippRegistrationInstanceA, "uac_register_no_cvs_300.xml")
regd = multiprocessing.Process(name='sipp1register', target=self.launchSippProcess,
args=(self.sippRegistrationInstanceA, 5064,))
regd.start()
regd.join()
# launch the sipp instance to register the second participant to asterisk
self.initialize_sipp_registration_instance(self.sippRegistrationInstanceB, "uac_register_no_cvs_400.xml")
regd = multiprocessing.Process(name='sipp2register', target=self.launchSippProcess,
args=(self.sippRegistrationInstanceB, 5066,))
regd.start()
regd.join()
# launch the sipp instance waining for call as the first participant
self.initialize_sipp_call_instance(self.sippCallInstanceA)
calldA = multiprocessing.Process(name='sipp1call', target=self.launchSippProcess,
args=(self.sippCallInstanceA, 5064,))
calldA.start()
# launch the sipp instance waiting for call as the second particpant
self.initialize_sipp_call_instance(self.sippCallInstanceB)
calldB = multiprocessing.Process(name='sipp2call', target=self.launchSippProcess,
args=(self.sippCallInstanceB, 5066,))
calldB.start()
# make sure every account are enabled
accList = [x for x in self.getAllAccounts() if x != "IP2IP"]
for acc in accList:
if not self.isAccountRegistered(acc):
self.setAccountEnable(acc, True)
# make a call to the SIPP instance
self.Call("300")
self.Call("400")
# start the main loop for processing glib callbacks
self.start()
print "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-"
calldA.join()
print "+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+"
calldB.join()
print "====================================================="
self.stopThread()
self.parse_results()
# callInstance = TestSFLPhoneRegisteredCalls()
# callInstance.test_registered_call()
confInstance = TestSFLPhoneConferenceCalls()
confInstance.test_conference_call()

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="ISO-8859-2" ?>
<!-- Use with CSV file struct like: 3000;192.168.1.106;[authentication username=3000 password=3000];
(user part of uri, server address, auth tag in each line)
-->
<scenario name="register_client">
<send retrans="500">
<![CDATA[
REGISTER sip:127.0.0.1 SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: <sip:300@127.0.0.1>;tag=[call_number]
To: <sip:400@127.0.0.1>
Call-ID: [call_id]
CSeq: [cseq] REGISTER
Contact: sip:300@[local_ip]:[local_port]
Max-Forwards: 10
Expires: 120
User-Agent: SIPp/Win32
Content-Length: 0
]]>
</send>
<!-- asterisk -->
<recv response="200">
<!--
<action>
<ereg regexp=".*" search_in="hdr" header="Contact:" check_it="true" assign_to="1" />
</action>
-->
</recv>
<!--
<recv request="INVITE" crlf="true">
</recv>
<send>
<![CDATA[
SIP/2.0 180 Ringing
[last_Via:]
[last_From:]
[last_To:];tag=[pid]SIPpTag01[call_number]
[last_Call-ID:]
[last_CSeq:]
Contact: <sip:[local_ip]:[local_port];transport=[transport]>
Content-Length: 0
]]>
</send>
<send retrans="500">
<![CDATA[
SIP/2.0 200 OK
[last_Via:]
[last_From:]
[last_To:];tag=[pid]SIPpTag01[call_number]
[last_Call-ID:]
[last_CSeq:]
Contact: <sip:[local_ip]:[local_port];transport=[transport]>
Content-Type: application/sdp
Content-Length: [len]
v=0
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
s=-
c=IN IP[media_ip_type] [media_ip]
t=0 0
m=audio [media_port] RTP/AVP 0
a=rtpmap:0 PCMU/8000
]]>
</send>
<recv request="ACK"
optional="true"
rtd="true"
crlf="true">
</recv>
<recv request="BYE">
</recv>
<send>
<![CDATA[
SIP/2.0 200 OK
[last_Via:]
[last_From:]
[last_To:]
[last_Call-ID:]
[last_CSeq:]
Contact: <sip:[local_ip]:[local_port];transport=[transport]>
Content-Length: 0
]]>
</send>
<timewait milliseconds="4000"/>
<ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
<CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
-->
</scenario>