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@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@default-00 (None) Down ()\n" + output += "Local/123@default-00 (None) Down ()\n" + output += "SIP/alice@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@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@bob,,T) + same => n,Hangup() + +exten => bob_atxfer,1,Dial(SIP/test_call@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@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@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' -- _____________________________________________________________________ -- Bandwidth and Colocation Provided by http://www.api-digital.com -- svn-commits mailing list To UNSUBSCRIBE or update options visit: http://lists.digium.com/mailman/listinfo/svn-commits
