[asterisk-commits] mmichelson: testsuite/asterisk/trunk r4639 - in /asterisk/trunk: lib/python/a...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Jan 31 16:33:48 CST 2014


Author: mmichelson
Date: Fri Jan 31 16:33:45 2014
New Revision: 4639

URL: http://svnview.digium.com/svn/testsuite?view=rev&rev=4639
Log:
Create pluggable module for PJSUA and PJSIP subscription tests.

In order to test subscriptions, a smarter client than what is provided
by SIPp is required. Using PJSUA is a good option since it can subscribe
to presence. In order to more easily create future PJSUA tests, I created
a pluggable module that allows for PJSUA accounts, transports, and buddies
to be created via YAML configuration.

In addition to the pluggable module, three tests have been added:

presence_pidf: Tests that PIDF bodies contain information we expect
presence_xpidf: Tests that XPIDF bodies contain information we expect
mwi: Tests that MWI bodies contain information we expect.

Review: https://reviewboard.asterisk.org/r/3151


Added:
    asterisk/trunk/tests/channels/pjsip/mwi/
    asterisk/trunk/tests/channels/pjsip/mwi/configs/
    asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/
    asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/modules.conf   (with props)
    asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/pjsip.conf   (with props)
    asterisk/trunk/tests/channels/pjsip/mwi/mwi_check.py   (with props)
    asterisk/trunk/tests/channels/pjsip/mwi/test-config.yaml   (with props)
    asterisk/trunk/tests/channels/pjsip/presence_pidf/
    asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/
    asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/
    asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/extensions.conf   (with props)
    asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/modules.conf   (with props)
    asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/pjsip.conf   (with props)
    asterisk/trunk/tests/channels/pjsip/presence_pidf/state_check.py   (with props)
    asterisk/trunk/tests/channels/pjsip/presence_pidf/test-config.yaml   (with props)
    asterisk/trunk/tests/channels/pjsip/presence_xpidf/
    asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/
    asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/
    asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/extensions.conf   (with props)
    asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/modules.conf   (with props)
    asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/pjsip.conf   (with props)
    asterisk/trunk/tests/channels/pjsip/presence_xpidf/state_check.py   (with props)
    asterisk/trunk/tests/channels/pjsip/presence_xpidf/test-config.yaml   (with props)
Modified:
    asterisk/trunk/lib/python/asterisk/pluggable_modules.py
    asterisk/trunk/tests/channels/pjsip/tests.yaml

Modified: asterisk/trunk/lib/python/asterisk/pluggable_modules.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/pluggable_modules.py?view=diff&rev=4639&r1=4638&r2=4639
==============================================================================
--- asterisk/trunk/lib/python/asterisk/pluggable_modules.py (original)
+++ asterisk/trunk/lib/python/asterisk/pluggable_modules.py Fri Jan 31 16:33:45 2014
@@ -10,10 +10,12 @@
 
 import sys
 import logging
+import socket
 
 sys.path.append("lib/python")
 from ami import AMIEventInstance
 from twisted.internet import reactor
+import pjsua as pj
 
 LOGGER = logging.getLogger(__name__)
 
@@ -261,3 +263,231 @@
             LOGGER.info("All channels have hungup; stopping test")
             self.test_object.stop_reactor()
         return (ami, event)
+
+
+class RegDetector(pj.AccountCallback):
+    """
+    Class that detects PJSUA account registration
+
+    This is a subclass of pj.AccountCallback and is set as the callback class
+    for PJSUA accounts by the pluggable module.
+
+    The only method that is overridden is the on_reg_state method, which is
+    called when the registration state of an account changes. When all
+    configured accounts have registered, then the configured callback method
+    for the test is called into.
+
+    This means that as written, all PJSUA tests require registration to be
+    performed.
+    """
+    def __init__(self, test_plugin):
+        self.test_plugin = test_plugin
+        pj.AccountCallback.__init__(self)
+
+    def on_reg_state(self):
+        """
+        Method that is called into when an account's registration state
+        changes.
+
+        If the registration status is in the 2XX range, then it means the
+        account has successfully registered with Asterisk. Once all configured
+        accounts have registered, this method will call the configured callback
+        method.
+
+        Since on_reg_state is called from PJSUA's thread, the ensuing callback
+        to the configured callback is pushed into the reactor thread.
+        """
+        status = self.account.info().reg_status
+        uri = self.account.info().uri
+
+        if status >= 200 and status < 300:
+            LOGGER.info("Detected successful registration from %s" % uri)
+            self.test_plugin.num_regs += 1
+
+        if self.test_plugin.num_regs == self.test_plugin.num_accts:
+            callback_module = __import__(self.test_plugin.callback_module)
+            callback_method = getattr(callback_module,
+                                      self.test_plugin.callback_method)
+            reactor.callFromThread(callback_method,
+                                   self.test_plugin.test_object,
+                                   self.test_plugin.pj_accounts)
+
+
+class PJsuaAccount(object):
+    """
+    Wrapper for pj.Account object
+
+    This object contains a reference to a pj.Account and a dictionary of the
+    account's buddies, keyed by buddy name
+    """
+    def __init__(self, account):
+        self.account = account
+        self.buddies = {}
+
+    def add_buddies(self, buddy_cfg):
+        """
+        Add configured buddies to the account.
+
+        All buddies are required to have a name and a URI set.
+        """
+        for buddy in buddy_cfg:
+            name = buddy.get('name')
+            if not name:
+                LOGGER.warning("Unable to add buddy with no name")
+                continue
+
+            uri = buddy.get('uri')
+            if not uri:
+                LOGGER.warning("Unable to add buddy %s. No URI", name)
+                continue
+
+            self.buddies[name] = self.account.add_buddy(uri)
+
+
+class PJsua(object):
+    """A class that takes care of the initialization and account creation for
+    PJSUA endpoints during a test.
+
+    This class will initiate PJLIB, create any configured accounts, and wait
+    for the accounts to register. Once registered, this will call into user
+    code so that manipulation of the endpoints may be performed.
+    """
+
+    def __init__(self, instance_config, test_object):
+        """Constructor for pluggable modules."""
+        super(PJsua, self).__init__()
+        self.test_object = test_object
+        self.test_object.register_ami_observer(self.__ami_connect)
+        self.config = instance_config
+        self.pj_transports = {}
+        self.pj_accounts = {}
+        self.lib = None
+        self.num_regs = 0
+        self.num_accts = 0
+        self.ami = None
+        self.acct_cb = RegDetector(self)
+        self.callback_module = instance_config['callback_module']
+        self.callback_method = instance_config['callback_method']
+
+    def __ami_connect(self, ami):
+        """
+        Handler for when AMI has started.
+
+        We use AMI connection as the signal to start creating PJSUA accounts
+        and starting PJLIB.
+        """
+        self.ami = ami
+        self.lib = pj.Lib()
+        try:
+            self.lib.init()
+            self.__create_transports()
+            self.lib.set_null_snd_dev()
+            self.__create_accounts()
+            self.lib.start()
+        except pj.Error, exception:
+            LOGGER.error("Exception: " + str(exception))
+            self.lib.destroy()
+            self.lib = None
+            self.test_object.stop_reactor()
+
+    def __create_transport(self, cfg):
+        """Create a PJSUA transport from a transport configuration."""
+        def __to_pjprotocol(prot_str, is_v6):
+            """
+            Translate a string protocol to an enumerated type for PJSUA.
+
+            PJSUA's enumerations require both the transport protocol to be used
+            and whether IPv6 is being used.
+            """
+            if prot_str == 'udp':
+                if is_v6:
+                    return pj.TransportType.UDP_IPV6
+                else:
+                    return pj.TransportType.UDP
+            elif prot_str == 'tcp':
+                if is_v6:
+                    return pj.TransportType.TCP_IPV6
+                else:
+                    return pj.TransportType.TCP
+            elif prot_str == 'tls':
+                if is_v6:
+                    LOGGER.error("PJSUA python bindings do not support IPv6"
+                                 "with TLS")
+                    self.test_object.stop_reactor()
+                else:
+                    return pj.TransportType.TLS
+            else:
+                return pj.TransportType.UNSPECIFIED
+
+        protocol = (cfg.get('protocol', 'udp')).lower()
+        bind = cfg.get('bind', '127.0.0.1')
+        bindport = cfg.get('bindport', '5060')
+        public_addr = cfg.get('public_addr', '')
+        is_v6 = False
+
+        try:
+            socket.inet_pton(socket.AF_INET6, bind)
+            is_v6 = True
+        except socket.error:
+            # Catching an exception just means the address is not IPv6
+            pass
+
+        pj_protocol = __to_pjprotocol(protocol, is_v6)
+        LOGGER.info("Creating transport config %s:%s" % (bind, bindport))
+        transport_cfg = pj.TransportConfig(int(bindport), bind, public_addr)
+        return self.lib.create_transport(pj_protocol, transport_cfg)
+
+    def __create_transports(self):
+        """
+        Create all configured transports
+
+        If no transports are configured, then a single transport, called
+        "default" will be created, using address 127.0.0.1, UDP port 5060.
+        """
+        if not self.config.get('transports'):
+            cfg = {
+                'name': 'default',
+            }
+            self.__create_transport(cfg)
+            return
+
+        for cfg in self.config['transports']:
+            if not cfg.get('name'):
+                LOGGER.error("No transport name specified")
+                self.test_object.stop_reactor()
+            self.pj_transports[cfg['name']] = self.__create_transport(cfg)
+
+    def __create_account(self, acct_cfg):
+        """Create a PJSuaAccount from configuration"""
+        name = acct_cfg['name']
+        username = acct_cfg.get('username', name)
+        domain = acct_cfg.get('domain', '127.0.0.1')
+        password = acct_cfg.get('password', '')
+
+        pj_acct_cfg = pj.AccountConfig(domain, username, password, name)
+
+        LOGGER.info("Creating PJSUA account %s@%s" % (username, domain))
+        account = PJsuaAccount(self.lib.create_account(pj_acct_cfg, False,
+                                                       self.acct_cb))
+        account.add_buddies(acct_cfg.get('buddies', []))
+        return account
+
+    def __create_accounts(self):
+        """
+        Create all configured PJSUA accounts.
+
+        All accounts must have a name specified. All other parameters will have
+        suitable defaults provided if not present. See the sample yaml file for
+        default values.
+        """
+        if not self.config.get('accounts'):
+            LOGGER.error("No accounts configured")
+            self.test_object.stop_reactor()
+
+        self.num_accts = len(self.config['accounts'])
+        for acct in self.config['accounts']:
+            name = acct.get('name')
+            if not name:
+                LOGGER.error("Account configuration has no name")
+                self.test_object.stop_reactor()
+            self.pj_accounts[name] = self.__create_account(acct)

Added: asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/modules.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/modules.conf?view=auto&rev=4639
==============================================================================
--- asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/modules.conf (added)
+++ asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/modules.conf Fri Jan 31 16:33:45 2014
@@ -1,0 +1,9 @@
+[modules]
+autoload=yes
+
+noload => chan_alsa.so
+noload => chan_oss.so
+noload => chan_console.so
+noload => chan_sip.so
+
+noload => app_voicemail.so

Propchange: asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/modules.conf
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/modules.conf
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/modules.conf
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/pjsip.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/pjsip.conf?view=auto&rev=4639
==============================================================================
--- asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/pjsip.conf (added)
+++ asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/pjsip.conf Fri Jan 31 16:33:45 2014
@@ -1,0 +1,15 @@
+[local-transport]
+type=transport
+bind=127.0.0.1
+protocol=udp
+
+[alice]
+type=endpoint
+allow=g722,ulaw,alaw
+context=default
+aors=alice
+mailboxes=alice
+
+[alice]
+type=aor
+max_contacts=5

Propchange: asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/pjsip.conf
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/pjsip.conf
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/tests/channels/pjsip/mwi/configs/ast1/pjsip.conf
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: asterisk/trunk/tests/channels/pjsip/mwi/mwi_check.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/pjsip/mwi/mwi_check.py?view=auto&rev=4639
==============================================================================
--- asterisk/trunk/tests/channels/pjsip/mwi/mwi_check.py (added)
+++ asterisk/trunk/tests/channels/pjsip/mwi/mwi_check.py Fri Jan 31 16:33:45 2014
@@ -1,0 +1,87 @@
+import logging
+import pjsua as pj
+
+LOGGER = logging.getLogger(__name__)
+
+
+class AliceCallback(pj.AccountCallback):
+    mwis = [
+        {'new': '2', 'old': '0'},
+        {'new': '1', 'old': '1'},
+        {'new': '0', 'old': '2'},
+    ]
+    results = [
+        {'waiting': 'yes', 'msgs': '2/0'},
+        {'waiting': 'yes', 'msgs': '1/1'},
+        {'waiting': 'no', 'msgs': '0/2'},
+        {'waiting': 'no', 'msgs': '0/0'},
+    ]
+
+    def __init__(self, alice, test_object):
+        pj.AccountCallback.__init__(self, alice)
+        self.pos = 0
+        self.result_pos = 0
+        self.test_object = test_object
+        self.ami = self.test_object.ami[0]
+        self.deleted = False
+
+    def check_mwi(self, body):
+        waiting = "Messages-Waiting: %s\r\n" % \
+            self.results[self.result_pos]['waiting']
+
+        msgs = "Voice-Message: %s (0/0)\r\n" % \
+            self.results[self.result_pos]['msgs']
+
+        if not waiting in body:
+            LOGGER.error("Could not find pattern %s in MWI body %s" %
+                         (waiting, body))
+            self.test_object.set_passed(False)
+            self.test_object.stop_reactor()
+        if not msgs in body:
+            LOGGER.error("Could not find pattern %s in MWI body %s" %
+                         (msgs, body))
+            self.test_object.set_passed(False)
+            self.test_object.stop_reactor()
+
+        self.result_pos += 1
+
+    def on_mwi_info(self, body):
+        self.check_mwi(body)
+        self.pos += 1
+        if (self.pos < len(self.mwis)):
+            self.send_mwi()
+            return
+
+        if (self.deleted):
+            self.test_object.set_passed(True)
+            self.test_object.stop_reactor()
+        else:
+            self.delete_mwi()
+            self.deleted = True
+
+    def send_mwi(self):
+        LOGGER.info("Sending MWI update. new: %s, old %s" %
+                    (self.mwis[self.pos]['new'],
+                     self.mwis[self.pos]['old']))
+        message = {
+            'Action': 'MWIUpdate',
+            'Mailbox': 'alice',
+            'NewMessages': self.mwis[self.pos]['new'],
+            'OldMessages': self.mwis[self.pos]['old']
+        }
+        self.ami.sendMessage(message)
+
+    def delete_mwi(self):
+        LOGGER.info("Deleting Mailbox")
+        message = {
+            'Action': 'MWIDelete',
+            'Mailbox': 'alice',
+        }
+        self.ami.sendMessage(message)
+
+
+def mwi_callback(test_object, accounts):
+    alice = accounts.get('alice')
+    cb = AliceCallback(alice, test_object)
+    alice.account.set_callback(cb)
+    cb.send_mwi()

Propchange: asterisk/trunk/tests/channels/pjsip/mwi/mwi_check.py
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/tests/channels/pjsip/mwi/mwi_check.py
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/tests/channels/pjsip/mwi/mwi_check.py
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: asterisk/trunk/tests/channels/pjsip/mwi/test-config.yaml
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/pjsip/mwi/test-config.yaml?view=auto&rev=4639
==============================================================================
--- asterisk/trunk/tests/channels/pjsip/mwi/test-config.yaml (added)
+++ asterisk/trunk/tests/channels/pjsip/mwi/test-config.yaml Fri Jan 31 16:33:45 2014
@@ -1,0 +1,46 @@
+testinfo:
+    summary:     'Ensures MWI bodies consist of accurate information'
+    description: |
+        "Unsolicited MWI notifications are sent to an endpoint as mailbox state updates. PJSUA
+        accounts notify us when an MWI notification arrives. We check the body of these MWI
+        notifications to ensure that they contain the data we expect them to."
+
+
+properties:
+    minversion: '12.0.0'
+    dependencies:
+        - python: 'twisted'
+        - python: 'starpy'
+        - python: 'pjsua'
+        - asterisk: 'res_pjsip'
+        - asterisk: 'res_pjsip_mwi'
+    tags:
+        - pjsip
+
+test-modules:
+    add-test-to-search-path: 'True'
+    test-object:
+        config-section: test-case-config
+        typename: 'test_case.TestCaseModule'
+    modules:
+        -
+            config-section: 'pjsua-config'
+            typename: 'pluggable_modules.PJsua'
+
+test-case-config:
+    connect-ami: 'True'
+
+pjsua-config:
+    callback_module: 'mwi_check'
+    callback_method: 'mwi_callback'
+    transports:
+        -
+            name: 'local-ipv4'
+            bind: '127.0.0.1'
+            bindport: '5061'
+    accounts:
+        -
+            name: 'alice'
+            username: 'alice'
+            password: 'alice'
+            domain: '127.0.0.1'

Propchange: asterisk/trunk/tests/channels/pjsip/mwi/test-config.yaml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/tests/channels/pjsip/mwi/test-config.yaml
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/tests/channels/pjsip/mwi/test-config.yaml
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/extensions.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/extensions.conf?view=auto&rev=4639
==============================================================================
--- asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/extensions.conf (added)
+++ asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/extensions.conf Fri Jan 31 16:33:45 2014
@@ -1,0 +1,3 @@
+[default]
+
+exten => bob,hint,Custom:bob

Propchange: asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/extensions.conf
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/extensions.conf
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/extensions.conf
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/modules.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/modules.conf?view=auto&rev=4639
==============================================================================
--- asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/modules.conf (added)
+++ asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/modules.conf Fri Jan 31 16:33:45 2014
@@ -1,0 +1,9 @@
+[modules]
+autoload=yes
+
+noload => chan_alsa.so
+noload => chan_oss.so
+noload => chan_console.so
+noload => chan_sip.so
+
+noload => res_pjsip_xpidf_body_generator.so

Propchange: asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/modules.conf
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/modules.conf
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/modules.conf
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/pjsip.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/pjsip.conf?view=auto&rev=4639
==============================================================================
--- asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/pjsip.conf (added)
+++ asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/pjsip.conf Fri Jan 31 16:33:45 2014
@@ -1,0 +1,14 @@
+[local-transport]
+type=transport
+bind=127.0.0.1
+protocol=udp
+
+[alice]
+type=endpoint
+allow=g722,ulaw,alaw
+context=default
+aors=alice
+
+[alice]
+type=aor
+max_contacts=5

Propchange: asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/pjsip.conf
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/pjsip.conf
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/tests/channels/pjsip/presence_pidf/configs/ast1/pjsip.conf
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: asterisk/trunk/tests/channels/pjsip/presence_pidf/state_check.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/pjsip/presence_pidf/state_check.py?view=auto&rev=4639
==============================================================================
--- asterisk/trunk/tests/channels/pjsip/presence_pidf/state_check.py (added)
+++ asterisk/trunk/tests/channels/pjsip/presence_pidf/state_check.py Fri Jan 31 16:33:45 2014
@@ -1,0 +1,69 @@
+#!/usr/bin/env python
+
+import logging
+import pjsua as pj
+
+LOGGER = logging.getLogger(__name__)
+
+states = [
+    ('INUSE', 2, "On the phone"),
+    ('ONHOLD', 2, "On hold"),
+    ('BUSY', 2, "On the phone"),
+    ('RINGING', 2, "Ringing"),
+    ('UNAVAILABLE', 2, "Unavailable"),
+    ('NOT_INUSE', 1, "Ready"),
+    ('', 1, "Ready")  # Final state upon subscription teardown
+]
+
+
+class BobCallback(pj.BuddyCallback):
+    def __init__(self, bob, test_object):
+        pj.BuddyCallback.__init__(self, bob)
+        self.pos = 0
+        self.test_object = test_object
+        self.ami = self.test_object.ami[0]
+        self.check_status = False
+
+    def on_state(self):
+        info = self.buddy.info()
+
+        LOGGER.info("Bob status to folow")
+        LOGGER.info("Online status: %d" % info.online_status)
+        LOGGER.info("Online text: %s" % info.online_text)
+        LOGGER.info("Activity: %d" % info.activity)
+        LOGGER.info("Sub state: %d" % info.sub_state)
+
+        # We don't care about the buddy state until the subscription is active.
+        if info.sub_state < pj.SubscriptionState.ACTIVE:
+            return
+
+        if self.check_status:
+            if info.online_status != states[self.pos][1]:
+                LOGGER.error("Unexpected state %d. Expected %d" %
+                             (info.online_status,  states[self.pos][1]))
+                self.test_object.set_passed(False)
+                self.test_object.stop_reactor(False)
+            if info.online_text != states[self.pos][2]:
+                LOGGER.error("Unexpected text %s. Expected %s" %
+                             (info.online_text, states[self.pos][2]))
+                self.test_object.set_passed(False)
+                self.test_object.stop_reactor(False)
+            self.pos += 1
+            if (self.pos >= len(states)):
+                self.test_object.set_passed(True)
+                self.test_object.stop_reactor()
+
+        if self.pos < len(states) and states[self.pos][0]:
+            LOGGER.info("Setting device state to %s" % states[self.pos][0])
+            self.check_status = True
+            self.ami.setVar(channel="", variable="DEVICE_STATE(Custom:bob)",
+                            value=states[self.pos][0])
+        else:
+            self.buddy.unsubscribe()
+
+
+def buddy_subscribe(test_object, accounts):
+    alice = accounts.get('alice')
+    bob = alice.buddies.get('bob')
+    bob.set_callback(BobCallback(bob, test_object))
+    bob.subscribe()

Propchange: asterisk/trunk/tests/channels/pjsip/presence_pidf/state_check.py
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/tests/channels/pjsip/presence_pidf/state_check.py
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/tests/channels/pjsip/presence_pidf/state_check.py
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: asterisk/trunk/tests/channels/pjsip/presence_pidf/test-config.yaml
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/pjsip/presence_pidf/test-config.yaml?view=auto&rev=4639
==============================================================================
--- asterisk/trunk/tests/channels/pjsip/presence_pidf/test-config.yaml (added)
+++ asterisk/trunk/tests/channels/pjsip/presence_pidf/test-config.yaml Fri Jan 31 16:33:45 2014
@@ -1,0 +1,50 @@
+testinfo:
+    summary:     'Test PJSIP PIDF presence notification'
+    description: |
+        'This test creates a PJSUA account, "alice", that subscribes to
+        buddy "bob". Asterisk changes the state of Bob to device states that
+        map to each of the extension states. We then ensure that PJSUA was
+        given the state we expected for each state change.'
+
+properties:
+    minversion: '12.1.0'
+    dependencies:
+        - python: 'twisted'
+        - python: 'starpy'
+        - python: 'pjsua'
+        - asterisk: 'res_pjsip'
+        - asterisk: 'res_pjsip_exten_state'
+    tags:
+        - pjsip
+
+test-modules:
+    add-test-to-search-path: 'True'
+    test-object:
+        config-section: test-case-config
+        typename: 'test_case.TestCaseModule'
+    modules:
+        -
+            config-section: 'pjsua-config'
+            typename: 'pluggable_modules.PJsua'
+
+test-case-config:
+    connect-ami: 'True'
+
+pjsua-config:
+    callback_module: 'state_check'
+    callback_method: 'buddy_subscribe'
+    transports:
+        -
+            name: 'local-ipv4'
+            bind: '127.0.0.1'
+            bindport: '5061'
+    accounts:
+        -
+            name: 'alice'
+            username: 'alice'
+            password: 'alice'
+            domain: '127.0.0.1'
+            buddies:
+                -
+                    name: 'bob'
+                    uri: 'sip:bob at 127.0.0.1'

Propchange: asterisk/trunk/tests/channels/pjsip/presence_pidf/test-config.yaml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/tests/channels/pjsip/presence_pidf/test-config.yaml
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/tests/channels/pjsip/presence_pidf/test-config.yaml
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/extensions.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/extensions.conf?view=auto&rev=4639
==============================================================================
--- asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/extensions.conf (added)
+++ asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/extensions.conf Fri Jan 31 16:33:45 2014
@@ -1,0 +1,3 @@
+[default]
+
+exten => bob,hint,Custom:bob

Propchange: asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/extensions.conf
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/extensions.conf
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/extensions.conf
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/modules.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/modules.conf?view=auto&rev=4639
==============================================================================
--- asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/modules.conf (added)
+++ asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/modules.conf Fri Jan 31 16:33:45 2014
@@ -1,0 +1,9 @@
+[modules]
+autoload=yes
+
+noload => chan_alsa.so
+noload => chan_oss.so
+noload => chan_console.so
+noload => chan_sip.so
+
+noload => res_pjsip_pidf_body_generator.so

Propchange: asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/modules.conf
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/modules.conf
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/modules.conf
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/pjsip.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/pjsip.conf?view=auto&rev=4639
==============================================================================
--- asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/pjsip.conf (added)
+++ asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/pjsip.conf Fri Jan 31 16:33:45 2014
@@ -1,0 +1,14 @@
+[local-transport]
+type=transport
+bind=127.0.0.1
+protocol=udp
+
+[alice]
+type=endpoint
+allow=g722,ulaw,alaw
+context=default
+aors=alice
+
+[alice]
+type=aor
+max_contacts=5

Propchange: asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/pjsip.conf
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/pjsip.conf
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/tests/channels/pjsip/presence_xpidf/configs/ast1/pjsip.conf
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: asterisk/trunk/tests/channels/pjsip/presence_xpidf/state_check.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/pjsip/presence_xpidf/state_check.py?view=auto&rev=4639
==============================================================================
--- asterisk/trunk/tests/channels/pjsip/presence_xpidf/state_check.py (added)
+++ asterisk/trunk/tests/channels/pjsip/presence_xpidf/state_check.py Fri Jan 31 16:33:45 2014
@@ -1,0 +1,69 @@
+#!/usr/bin/env python
+
+import logging
+import pjsua as pj
+
+LOGGER = logging.getLogger(__name__)
+
+states = [
+    ('INUSE', 2, "Offline"),
+    ('ONHOLD', 2, "Offline"),
+    ('BUSY', 2, "Offline"),
+    ('RINGING', 2, "Offline"),
+    ('UNAVAILABLE', 2, "Offline"),
+    ('NOT_INUSE', 1, "Online"),
+    ('', 1, "Online")  # Final state upon subscription teardown
+]
+
+
+class BobCallback(pj.BuddyCallback):
+    def __init__(self, bob, test_object):
+        pj.BuddyCallback.__init__(self, bob)
+        self.pos = 0
+        self.test_object = test_object
+        self.ami = self.test_object.ami[0]
+        self.check_status = False
+
+    def on_state(self):
+        info = self.buddy.info()
+
+        LOGGER.info("Bob status to folow")
+        LOGGER.info("Online status: %d" % info.online_status)
+        LOGGER.info("Online text: %s" % info.online_text)
+        LOGGER.info("Activity: %d" % info.activity)
+        LOGGER.info("Sub state: %d" % info.sub_state)
+
+        # We don't care about the buddy state until the subscription is active.
+        if info.sub_state < pj.SubscriptionState.ACTIVE:
+            return
+
+        if self.check_status:
+            if info.online_status != states[self.pos][1]:
+                LOGGER.error("Unexpected state %d. Expected %d" %
+                             (info.online_status,  states[self.pos][1]))
+                self.test_object.set_passed(False)
+                self.test_object.stop_reactor(False)
+            if info.online_text != states[self.pos][2]:
+                LOGGER.error("Unexpected text %s. Expected %s" %
+                             (info.online_text, states[self.pos][2]))
+                self.test_object.set_passed(False)
+                self.test_object.stop_reactor()
+            self.pos += 1
+            if (self.pos >= len(states)):
+                self.test_object.set_passed(True)
+                self.test_object.stop_reactor()
+
+        if self.pos < len(states) and states[self.pos][0]:
+            LOGGER.info("Setting device state to %s" % states[self.pos][0])
+            self.check_status = True
+            self.ami.setVar(channel="", variable="DEVICE_STATE(Custom:bob)",
+                            value=states[self.pos][0])
+        else:
+            self.buddy.unsubscribe()
+
+
+def buddy_subscribe(test_object, accounts):
+    alice = accounts.get('alice')
+    bob = alice.buddies.get('bob')
+    bob.set_callback(BobCallback(bob, test_object))
+    bob.subscribe()

Propchange: asterisk/trunk/tests/channels/pjsip/presence_xpidf/state_check.py
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/tests/channels/pjsip/presence_xpidf/state_check.py
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/tests/channels/pjsip/presence_xpidf/state_check.py
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: asterisk/trunk/tests/channels/pjsip/presence_xpidf/test-config.yaml
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/pjsip/presence_xpidf/test-config.yaml?view=auto&rev=4639
==============================================================================
--- asterisk/trunk/tests/channels/pjsip/presence_xpidf/test-config.yaml (added)
+++ asterisk/trunk/tests/channels/pjsip/presence_xpidf/test-config.yaml Fri Jan 31 16:33:45 2014
@@ -1,0 +1,49 @@
+testinfo:
+    summary:     'Test PJSIP XPIDF presence notification'
+    description: |
+        'This test creates a PJSUA account, "alice", that subscribes to
+        buddy "bob". Asterisk changes the state of Bob to device states that
+        map to each of the extension states. We then ensure that PJSUA was
+        given the state we expected for each state change.'
+
+properties:
+    minversion: '12.1.0'
+    dependencies:
+        - python: 'twisted'
+        - python: 'starpy'
+        - python: 'pjsua'
+        - asterisk: 'res_pjsip'
+    tags:
+        - pjsip
+
+test-modules:
+    add-test-to-search-path: 'True'
+    test-object:
+        config-section: test-case-config
+        typename: 'test_case.TestCaseModule'
+    modules:
+        -
+            config-section: 'pjsua-config'
+            typename: 'pluggable_modules.PJsua'
+
+test-case-config:
+    connect-ami: 'True'
+
+pjsua-config:
+    callback_module: 'state_check'
+    callback_method: 'buddy_subscribe'
+    transports:
+        -
+            name: 'local-ipv4'
+            bind: '127.0.0.1'
+            bindport: '5061'
+    accounts:
+        -
+            name: 'alice'
+            username: 'alice'
+            password: 'alice'
+            domain: '127.0.0.1'
+            buddies:
+                -
+                    name: 'bob'
+                    uri: 'sip:bob at 127.0.0.1'

Propchange: asterisk/trunk/tests/channels/pjsip/presence_xpidf/test-config.yaml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/tests/channels/pjsip/presence_xpidf/test-config.yaml
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/tests/channels/pjsip/presence_xpidf/test-config.yaml

[... 16 lines stripped ...]



More information about the asterisk-commits mailing list