[asterisk-dev] Change in testsuite[master]: Add SIP attended transfer for Asterisk 11.

Mark Michelson (Code Review) asteriskteam at digium.com
Mon Mar 30 17:10:30 CDT 2015


Mark Michelson has uploaded a new change for review.

  https://gerrit.asterisk.org/20

Change subject: Add SIP attended transfer for Asterisk 11.
......................................................................

Add SIP attended transfer for Asterisk 11.

The structure of this is similar to the new Asterisk 12+ SIP attended
transfer test, except that the method of determining if channels are
bridged is different.

Like the 12+ version, the two major changes to the original test are
the use of the PJSUA testsuite module and waiting to move forward
with test actions until PJSUA has informed us of state changes.

The logic used for determining bridgedness is the same as was used
in the sip_attended_transfer test that is being replaced.

Change-Id: I48c7b6a9298552aa756d0c2f26afbd6a96d553b5
---
A tests/channels/SIP/sip_attended_transfer_11/attended_transfer.py
A tests/channels/SIP/sip_attended_transfer_11/configs/ast1/extensions.conf
A tests/channels/SIP/sip_attended_transfer_11/configs/ast1/sip.conf
A tests/channels/SIP/sip_attended_transfer_11/test-config.yaml
M tests/channels/SIP/tests.yaml
5 files changed, 273 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/testsuite refs/changes/20/20/1

diff --git a/tests/channels/SIP/sip_attended_transfer_11/attended_transfer.py b/tests/channels/SIP/sip_attended_transfer_11/attended_transfer.py
new file mode 100644
index 0000000..5f22af4
--- /dev/null
+++ b/tests/channels/SIP/sip_attended_transfer_11/attended_transfer.py
@@ -0,0 +1,187 @@
+"""
+Copyright (C) 2015, Digium, Inc.
+Mark Michelson <mmichelson at digium.com>
+
+This program is free software, distributed under the terms of
+the GNU General Public License Version 2.
+"""
+
+import logging
+import pjsua as pj
+
+from twisted.internet import reactor
+
+LOGGER = logging.getLogger(__name__)
+
+INIT = 0
+BOB_CALLED = 1
+CAROL_CALLED = 2
+TRANSFERRED = 3
+
+
+class TransferAccountCallback(pj.AccountCallback):
+    '''Generic Account callback for Bob and Carol.
+
+    The sole purpose of this callback is to auto-answer
+    incoming calls
+    '''
+
+    def __init__(self, account):
+        pj.AccountCallback.__init__(self, account)
+
+    def on_incoming_call(self, call):
+        call.answer(200)
+
+
+class BobCallCallback(pj.CallCallback):
+    '''Call Callback used for Alice's call to Bob
+
+    When we get told the call state is CONFIRMED, we signal
+    to the test that the call is answered
+    '''
+
+    def __init__(self, call, transfer_object):
+        pj.CallCallback.__init__(self, call)
+        self.transfer_object = transfer_object
+
+    def on_state(self):
+        if self.call.info().state == pj.CallState.CONFIRMED:
+            reactor.callFromThread(self.transfer_object.bob_call_answered)
+
+
+class CarolCallCallback(pj.CallCallback):
+    '''Call Callback for Alice's call to Carol
+
+    When we get told the call state is CONFIRMED we signal to the test that the
+    call has been answered. This is very important, because if we attempt to
+    perform the transfer before PJSUA has set the call state to CONFIRMED, then
+    the REFER request that PJSUA sends will have a blank to-tag in the Refer-To
+    header. Waiting ensures that all information is present in the REFER
+    request.
+    '''
+
+    def __init__(self, call, transfer_object):
+        pj.CallCallback.__init__(self, call)
+        self.transfer_object = transfer_object
+
+    def on_state(self):
+        if self.call.info().state == pj.CallState.CONFIRMED:
+            reactor.callFromThread(self.transfer_object.carol_call_answered)
+
+
+class Transfer(object):
+    '''Controller for attended transfer test
+
+    This contains all the methods for advancing the test, such as placing calls
+    and performing transfers. It also has several state variables that help to
+    determine the proper timing for performing actions.
+    '''
+
+    def __init__(self, test_object, accounts):
+        super(Transfer, self).__init__()
+        self.ami = test_object.ami[0]
+        self.ami.registerEvent('Bridge', self.bridge)
+        self.ami.registerEvent('VarSet', self.bridge_peer)
+
+        self.chans = []
+        self.final_bridge = 0
+        self.bob_call_up = False
+        self.carol_call_up = False
+        self.bridge1_bridged = False
+        self.bridge2_bridged = False
+
+        bob = accounts.get('bob').account
+        bob.set_callback(TransferAccountCallback(bob))
+
+        carol = accounts.get('carol').account
+        carol.set_callback(TransferAccountCallback(carol))
+
+        self.alice = accounts.get('alice').account
+        self.call_to_bob = None
+        self.call_to_carol = None
+
+        self.test_object = test_object
+        self.state = INIT
+
+    def bridge(self, ami, event):
+        if event['channel2'] in self.chans:
+            return
+
+        self.chans.append(event['channel2'])
+        numchans = len(self.chans)
+        if numchans == 1:
+            self.bridge1_bridged = True
+            self.call_carol()
+        elif numchans == 2:
+            self.bridge2_bridged = True
+            self.transfer_call()
+
+    def bridge_peer(self, ami, event):
+        if event['variable'] != "BRIDGEPEER" or len(self.chans) < 2:
+            return
+
+        LOGGER.info("Inspecting BRIDGEPEER VarSet")
+
+        # we should get 2 bridgepeers with swapped channel and value headers
+        # indicating the bridged channels
+        if self.chans[:2] == [event['channel'], event['value']] or\
+            self.chans[:2] == [event['value'], event['channel']]:
+            LOGGER.info("Got expected VarSet")
+            self.final_bridge += 1
+            if self.final_bridge == 2:
+                LOGGER.info("Transfer successful!")
+                # success!
+                self.hangup_calls()
+
+    def bob_call_answered(self):
+        self.bob_call_up = True
+        self.call_carol()
+
+    def carol_call_answered(self):
+        self.carol_call_up = True
+        self.transfer_call()
+
+    def call_bob(self):
+        self.call_to_bob = self.alice.make_call('sip:bob at 127.0.0.1',
+                                                BobCallCallback(None, self))
+        self.state = BOB_CALLED
+
+    def call_carol(self):
+        if (self.state == BOB_CALLED and self.bridge1_bridged and
+                self.bob_call_up):
+            self.call_to_carol = self.alice.make_call('sip:carol at 127.0.0.1',
+                                                      CarolCallCallback(None,
+                                                                        self))
+            self.state = CAROL_CALLED
+
+    def transfer_call(self):
+        if (self.state == CAROL_CALLED and self.bridge2_bridged and
+                self.carol_call_up):
+            self.call_to_bob.transfer_to_call(self.call_to_carol)
+            self.state = TRANSFERRED
+
+    def hangup_calls(self):
+        bob_hangup = {
+            'Action': 'Hangup',
+            'Channel': '/SIP/bob-.*/',
+        }
+        carol_hangup = {
+            'Action': 'Hangup',
+            'Channel': '/SIP/carol-.*/',
+        }
+        self.ami.sendMessage(bob_hangup)
+        self.ami.sendMessage(carol_hangup)
+        self.test_object.set_passed(True)
+        self.test_object.stop_reactor()
+
+
+def phones_registered(test_object, accounts):
+    '''Entry point for attended transfer test
+
+    When the PJSUA module has detected that all phones have registered, this
+    method is called into. This initializes the test controller and sets the
+    test in motion by placing the first call of the test
+    '''
+
+    transfer = Transfer(test_object, accounts)
+    transfer.call_bob()
diff --git a/tests/channels/SIP/sip_attended_transfer_11/configs/ast1/extensions.conf b/tests/channels/SIP/sip_attended_transfer_11/configs/ast1/extensions.conf
new file mode 100644
index 0000000..e0390a2
--- /dev/null
+++ b/tests/channels/SIP/sip_attended_transfer_11/configs/ast1/extensions.conf
@@ -0,0 +1,7 @@
+[general]
+
+[globals]
+
+[transfertest]
+exten => bob,1,Dial(SIP/bob)
+exten => carol,1,Dial(SIP/carol)
diff --git a/tests/channels/SIP/sip_attended_transfer_11/configs/ast1/sip.conf b/tests/channels/SIP/sip_attended_transfer_11/configs/ast1/sip.conf
new file mode 100644
index 0000000..3169e03
--- /dev/null
+++ b/tests/channels/SIP/sip_attended_transfer_11/configs/ast1/sip.conf
@@ -0,0 +1,19 @@
+[general]
+canreinvite=no
+sipdebug=yes
+
+[alice]
+context=transfertest
+type=friend
+insecure=invite
+host=dynamic
+
+[bob]
+type=friend
+host=dynamic
+insecure=invite
+
+[carol]
+type=friend
+host=dynamic
+insecure=invite
diff --git a/tests/channels/SIP/sip_attended_transfer_11/test-config.yaml b/tests/channels/SIP/sip_attended_transfer_11/test-config.yaml
new file mode 100644
index 0000000..80dabb2
--- /dev/null
+++ b/tests/channels/SIP/sip_attended_transfer_11/test-config.yaml
@@ -0,0 +1,59 @@
+testinfo:
+    summary:     'Test SIP Attended Transfer'
+    description: |
+        'This test sets up three PJSUA accounts: Alice, Bob, and Carol. The test commences
+         as follows:
+             * Alice places a call through Asterisk to Bob.
+             * Once Alice and Bob are bridged, Alice places a call through Asterisk to Carol.
+             * Once Alice and Carol are bridged, Alice performs an attended transfer.
+             * The test ensures that Bob and Carol are bridged and that Alice is hung up.'
+
+test-modules:
+    add-test-to-search-path: True
+    test-object:
+        config-section: test-object-config
+        typename: 'test_case.TestCaseModule'
+    modules:
+        -
+            config-section: 'pjsua-config'
+            typename: 'pjsua_mod.PJsua'
+
+test-object-config:
+    connect-ami: True
+
+pjsua-config:
+    callback_module: 'attended_transfer'
+    callback_method: 'phones_registered'
+    transports:
+        -
+            name: 'local-ipv4'
+            bind: '127.0.0.1'
+            bindport: '5061'
+    accounts:
+        -
+            name: 'alice'
+            username: 'alice'
+            domain: '127.0.0.1'
+            transport: 'local-ipv4'
+        -
+            name: 'bob'
+            username: 'bob'
+            domain: '127.0.0.1'
+            transport: 'local-ipv4'
+        -
+            name: 'carol'
+            username: 'carol'
+            domain: '127.0.0.1'
+            transport: 'local-ipv4'
+
+properties:
+    minversion: '11.0.0'
+    maxversion: '12.0.0'
+    dependencies:
+        - python : 'twisted'
+        - python : 'starpy'
+        - python : 'pjsua'
+        - asterisk : 'chan_sip'
+    tags:
+        - SIP
+        - transfer
diff --git a/tests/channels/SIP/tests.yaml b/tests/channels/SIP/tests.yaml
index 9524a68..fe77452 100644
--- a/tests/channels/SIP/tests.yaml
+++ b/tests/channels/SIP/tests.yaml
@@ -72,3 +72,4 @@
     - test: 'invite_retransmit'
     - test: 'no_ack_dialog_cleanup'
     - test: 'no_reinvite_after_491'
+    - test: 'sip_attended_transfer_11'

-- 
To view, visit https://gerrit.asterisk.org/20
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I48c7b6a9298552aa756d0c2f26afbd6a96d553b5
Gerrit-PatchSet: 1
Gerrit-Project: testsuite
Gerrit-Branch: master
Gerrit-Owner: Mark Michelson <mmichelson at digium.com>



More information about the asterisk-dev mailing list