[svn-commits] sgriepentrog: testsuite/asterisk/trunk r6227 - in /asterisk/trunk: lib/python...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Fri Jan 9 15:57:36 CST 2015


Author: sgriepentrog
Date: Fri Jan  9 15:57:32 2015
New Revision: 6227

URL: http://svnview.digium.com/svn/testsuite?view=rev&rev=6227
Log:
testsuite: check for channel leak on failed blonde transfer

Tests attended transfer converted to blind by hangup of
the transferrer (blonde transfer), but where the target
of the transfer is then later unavailable, which causes
the transfer to fail and attempt to recall the original
transferer.   After the recall is answered, the list of
active channels is checked for a leaked reference.

ASTERISK-24513
Review: https://reviewboard.asterisk.org/r/4256/


Added:
    asterisk/trunk/tests/bridge/atxfer_fail_blonde/
    asterisk/trunk/tests/bridge/atxfer_fail_blonde/configs/
    asterisk/trunk/tests/bridge/atxfer_fail_blonde/configs/ast1/
    asterisk/trunk/tests/bridge/atxfer_fail_blonde/configs/ast1/extensions.conf   (with props)
    asterisk/trunk/tests/bridge/atxfer_fail_blonde/test-config.yaml   (with props)
Modified:
    asterisk/trunk/lib/python/asterisk/channel_test_condition.py
    asterisk/trunk/tests/bridge/tests.yaml

Modified: asterisk/trunk/lib/python/asterisk/channel_test_condition.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/channel_test_condition.py?view=diff&rev=6227&r1=6226&r2=6227
==============================================================================
--- asterisk/trunk/lib/python/asterisk/channel_test_condition.py (original)
+++ asterisk/trunk/lib/python/asterisk/channel_test_condition.py Fri Jan  9 15:57:32 2015
@@ -10,6 +10,10 @@
 
 from twisted.internet import defer
 from test_conditions import TestCondition
+import logging
+import unittest
+import re
+
 
 class ChannelTestCondition(TestCondition):
     """Test condition that checks for the existence of channels.  If channels
@@ -44,17 +48,27 @@
         def __channel_callback(result):
             """Callback called from core show channels"""
 
+            channel_expression = re.compile('^[A-Za-z0-9]+/')
             channel_tokens = result.output.strip().split('\n')
             active_channels = 0
+            referenced_channels = 0
             for token in channel_tokens:
+                if channel_expression.match(token):
+                    referenced_channels += 1
                 if 'active channels' in token:
                     active_channel_tokens = token.partition(' ')
                     active_channels = int(active_channel_tokens[0].strip())
             if active_channels > self.allowed_channels:
                 msg = ("Detected number of active channels %d is greater than "
-                       "the allowed %d on Asterisk %s" % (active_channels,
-                                                          self.allowed_channels,
-                                                          result.host))
+                       "the allowed %d on Asterisk %s" %
+                       (active_channels, self.allowed_channels, result.host))
+                super(ChannelTestCondition, self).fail_check(msg)
+            elif referenced_channels > self.allowed_channels:
+                msg = ("Channel leak detected - "
+                       "number of referenced channels %d is greater than "
+                       "the allowed %d on Asterisk %s" %
+                       (referenced_channels, self.allowed_channels,
+                        result.host))
                 super(ChannelTestCondition, self).fail_check(msg)
             return result
 
@@ -67,7 +81,160 @@
         # Set to pass and let a failure override
         super(ChannelTestCondition, self).pass_check()
 
-        exec_list = [ast.cli_exec('core show channels').addCallback(__channel_callback) for ast in self.ast]
+        exec_list = [ast.cli_exec('core show channels').
+                     addCallback(__channel_callback) for ast in self.ast]
         defer.DeferredList(exec_list).addCallback(_raise_finished,
                                                   finish_deferred)
         return finish_deferred
+
+
+class AstMockOutput(object):
+    """mock cli output base class"""
+
+    def __init__(self):
+        """Constructor"""
+        self.host = "127.0.0.1"
+
+    def MockDefer(self, output):
+        """use real defer to mock deferred output"""
+        self.output = output
+        deferred = defer.Deferred()
+        deferred.callback(self)
+        return deferred
+
+
+class AstMockObjectInactive(AstMockOutput):
+    """mock cli output showing no active channels"""
+
+    def cli_exec(self, command):
+        """presume command is core show channels and generate output"""
+        output = ""
+        output += "Channel              Location             State   Application(Data)\n"
+        output += "0 active channels\n"
+        output += "0 active calls\n"
+        output += "2 calls processed\n"
+        output += "Asterisk ending (0).\n"
+        return self.MockDefer(output)
+
+
+class AstMockObjectSingle(AstMockOutput):
+    """mock cli output showing single active channel"""
+
+    def cli_exec(self, command):
+        """presume command is core show channels and generate output"""
+        output = ""
+        output += "Channel              Location             State   Application(Data)\n"
+        output += "Local/123 at default-00 (None)               Down    ()\n"
+        output += "1 active channels\n"
+        output += "0 active calls\n"
+        output += "2 calls processed\n"
+        output += "Asterisk ending (0).\n"
+        return self.MockDefer(output)
+
+
+class AstMockObjectMultiple(AstMockOutput):
+    """mock cli output showing multiple active channels"""
+
+    def cli_exec(self, command):
+        """presume command is core show channels and generate output"""
+        output = ""
+        output += "Channel              Location             State   Application(Data)\n"
+        output += "PJSIP/123 at default-00 (None)               Down    ()\n"
+        output += "Local/123 at default-00 (None)               Down    ()\n"
+        output += "SIP/alice at default-00 (None)               Down    ()\n"
+        output += "3 active channels\n"
+        output += "0 active calls\n"
+        output += "2 calls processed\n"
+        output += "Asterisk ending (0).\n"
+        return self.MockDefer(output)
+
+
+class AstMockObjectLeaked(AstMockOutput):
+    """mock cli output showing leaked channel"""
+
+    def cli_exec(self, command):
+        """presume command is core show channels and generate output"""
+        output = ""
+        output += "Channel              Location             State   Application(Data)\n"
+        output += "Local/123 at default-00 (None)               Down    ()\n"
+        output += "0 active channels\n"
+        output += "0 active calls\n"
+        output += "2 calls processed\n"
+        output += "Asterisk ending (0).\n"
+        return self.MockDefer(output)
+
+
+class TestConfig(object):
+    """Fake TestConfig object for unittest"""
+
+    def __init__(self):
+        self.class_type_name = "bogus"
+        self.config = {}
+        self.enabled = True
+        self.pass_expected = True
+
+
+class ChannelTestConditionUnitTest(unittest.TestCase):
+    """Unit Tests for ChannelTestCondition"""
+
+    def test_evaluate_inactive(self):
+        """test inactive channel condition"""
+        obj = ChannelTestCondition(TestConfig())
+        obj.register_asterisk_instance(AstMockObjectInactive())
+        obj.evaluate()
+        self.assertEqual(obj.get_status(), 'Passed')
+
+    def test_evaluate_multiple_fail(self):
+        """test multiple channel condition"""
+        obj = ChannelTestCondition(TestConfig())
+        obj.register_asterisk_instance(AstMockObjectMultiple())
+        obj.evaluate()
+        self.assertEqual(obj.get_status(), 'Failed')
+
+    def test_evaluate_multiple_fail2(self):
+        """test multiple channel condition"""
+        obj = ChannelTestCondition(TestConfig())
+        obj.allowed_channels = 2
+        obj.register_asterisk_instance(AstMockObjectMultiple())
+        obj.evaluate()
+        self.assertEqual(obj.get_status(), 'Failed')
+
+    def test_evaluate_multiple_pass(self):
+        """test multiple channel condition"""
+        obj = ChannelTestCondition(TestConfig())
+        obj.allowed_channels = 3
+        obj.register_asterisk_instance(AstMockObjectMultiple())
+        obj.evaluate()
+        self.assertEqual(obj.get_status(), 'Passed')
+
+    def test_evaluate_single_fail(self):
+        """test single channel condition"""
+        obj = ChannelTestCondition(TestConfig())
+        obj.register_asterisk_instance(AstMockObjectSingle())
+        obj.evaluate()
+        self.assertEqual(obj.get_status(), 'Failed')
+
+    def test_evaluate_single_pass(self):
+        """test single channel condition"""
+        obj = ChannelTestCondition(TestConfig())
+        obj.allowed_channels = 1
+        obj.register_asterisk_instance(AstMockObjectSingle())
+        obj.evaluate()
+        self.assertEqual(obj.get_status(), 'Passed')
+
+    def test_evaluate_leaked(self):
+        """test leaked channel condition"""
+        obj = ChannelTestCondition(TestConfig())
+        obj.register_asterisk_instance(AstMockObjectLeaked())
+        obj.evaluate()
+        self.assertEqual(obj.get_status(), 'Failed')
+
+
+def main():
+    """Run the unit tests"""
+
+    logging.basicConfig(level=logging.DEBUG)
+    unittest.main()
+
+if __name__ == "__main__":
+    main()

Added: asterisk/trunk/tests/bridge/atxfer_fail_blonde/configs/ast1/extensions.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/bridge/atxfer_fail_blonde/configs/ast1/extensions.conf?view=auto&rev=6227
==============================================================================
--- asterisk/trunk/tests/bridge/atxfer_fail_blonde/configs/ast1/extensions.conf (added)
+++ asterisk/trunk/tests/bridge/atxfer_fail_blonde/configs/ast1/extensions.conf Fri Jan  9 15:57:32 2015
@@ -1,0 +1,10 @@
+[default]
+
+exten => alice_atxfer,1,Dial(SIP/test_call at bob,,T)
+	same => n,Hangup()
+
+exten => bob_atxfer,1,Dial(SIP/test_call at bob,,t)
+	same => n,Hangup()
+
+exten => 123,1,NoOp(This is the transfer target aka Charlie)
+	same => n,Echo()

Propchange: asterisk/trunk/tests/bridge/atxfer_fail_blonde/configs/ast1/extensions.conf
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/tests/bridge/atxfer_fail_blonde/configs/ast1/extensions.conf
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/tests/bridge/atxfer_fail_blonde/configs/ast1/extensions.conf
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: asterisk/trunk/tests/bridge/atxfer_fail_blonde/test-config.yaml
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/bridge/atxfer_fail_blonde/test-config.yaml?view=auto&rev=6227
==============================================================================
--- asterisk/trunk/tests/bridge/atxfer_fail_blonde/test-config.yaml (added)
+++ asterisk/trunk/tests/bridge/atxfer_fail_blonde/test-config.yaml Fri Jan  9 15:57:32 2015
@@ -1,0 +1,83 @@
+testinfo:
+    summary: 'Test blonde transfer to a failed destination'
+    description: |
+        'Checks for channel leak after an attended transfer that switches
+        to blonde mode and fails to connect to the target destination.
+        1) Alice calls Bob
+        2) Alice performs DTMF attended transfer to Charlie (123)
+        3) Charlie delays slightly...
+        4) Alice hangs up, converting attended transfer to blonde mode
+        5)                        ...and hangs up to fail transfer
+        6) When the transfer recall is answered by Alice, hang up
+        7) Check "core show channels" for leaked Local/123 channel
+        '
+
+test-modules:
+    add-test-to-search-path: 'True'
+    test-object:
+        config-section: 'bridge-config'
+        typename: 'bridge_test_case.BridgeTestCase'
+    modules:
+        -
+            config-section: 'pluggable-config'
+            typename: 'pluggable_modules.EventActionModule'
+
+bridge-config:
+    test-runs:
+      -
+          originate_channel: 'SIP/alice_atxfer at uut'
+          hangup: 'alice'
+          features:
+              -
+                  who: 'alice'
+                  what: 'atxfer'
+                  success: 'true'
+                  exten: '123'
+
+pluggable-config:
+    -
+        # after Alice actives blonde transfer
+        ami-events:
+            conditions:
+                match:
+                    Event: CEL
+                    EventName: ATTENDEDTRANSFER
+                    Channel: 'SIP/alice-00000000'
+        # hangup the transfer destination
+        ami-actions:
+            -
+                action:
+                    Action: 'Hangup'
+                    Channel: 'Local/123 at default-00000000;1'
+                    Cause: 17
+
+    -
+        # after transfer recall calls back Alice
+        ami-events:
+            conditions:
+                match:
+                    Event: CEL
+                    EventName: ANSWER
+                    Channel: 'SIP/alice-00000002'
+        # hang the call up, test is done
+        ami-actions:
+            -
+                action:
+                    Action: 'Hangup'
+                    Channel: 'SIP/alice-00000002'
+                    Cause: 16
+
+properties:
+    minversion: '11.0.0'
+    dependencies:
+        - buildoption: 'TEST_FRAMEWORK'
+        - python : 'twisted'
+        - python : 'starpy'
+        - asterisk : 'chan_sip'
+    tags:
+        - bridge
+    testconditions:
+        -
+            # this checks 'core show channels' for leaked channel
+            name: 'channels'
+            allowedchannels: 0

Propchange: asterisk/trunk/tests/bridge/atxfer_fail_blonde/test-config.yaml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/tests/bridge/atxfer_fail_blonde/test-config.yaml
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/tests/bridge/atxfer_fail_blonde/test-config.yaml
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: asterisk/trunk/tests/bridge/tests.yaml
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/bridge/tests.yaml?view=diff&rev=6227&r1=6226&r2=6227
==============================================================================
--- asterisk/trunk/tests/bridge/tests.yaml (original)
+++ asterisk/trunk/tests/bridge/tests.yaml Fri Jan  9 15:57:32 2015
@@ -3,6 +3,7 @@
     - test: 'disconnect'
     - test: 'atxfer_setup'
     - test: 'atxfer_nominal'
+    - test: 'atxfer_fail_blonde'
     - test: 'blindxfer_setup'
     - test: 'blindxfer_nominal'
     - test: 'blonde_nominal'




More information about the svn-commits mailing list