[svn-commits] mjordan: testsuite/asterisk/trunk r3213 - in /asterisk/trunk: lib/python/aste...
SVN commits to the Digium repositories
svn-commits at lists.digium.com
Tue May 1 08:32:41 CDT 2012
Author: mjordan
Date: Tue May 1 08:32:32 2012
New Revision: 3213
URL: http://svnview.digium.com/svn/testsuite?view=rev&rev=3213
Log:
Add ConfBridge Recording Test
This test checks for recording of a conference in ConfBridge via
the MixMonitor application. It tests enabling and setting the location
of the recording via the CONFBRIDGE function and via the confbridge.conf
configuration file. After a conference is recorded, the test verifies
that the recorded file contains some audio via the BackgroundDetect
application.
Review: https://reviewboard.asterisk.org/r/1880/
Added:
asterisk/trunk/tests/apps/confbridge/confbridge_recording/
asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/
asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/
asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/confbridge.conf (with props)
asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/extensions.conf (with props)
asterisk/trunk/tests/apps/confbridge/confbridge_recording/run-test (with props)
asterisk/trunk/tests/apps/confbridge/confbridge_recording/test-config.yaml (with props)
Modified:
asterisk/trunk/lib/python/asterisk/confbridge.py
asterisk/trunk/tests/apps/confbridge/tests.yaml
Modified: asterisk/trunk/lib/python/asterisk/confbridge.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/confbridge.py?view=diff&rev=3213&r1=3212&r2=3213
==============================================================================
--- asterisk/trunk/lib/python/asterisk/confbridge.py (original)
+++ asterisk/trunk/lib/python/asterisk/confbridge.py Tue May 1 08:32:32 2012
@@ -133,9 +133,9 @@
audioFile The local path to the file to stream
"""
if call_id in self.calls:
- logger.debug("Attempting to send audio file %s via %s", audioFile, call_id)
+ logger.debug("Attempting to send audio file %s via %s", audioFile, self.calls[call_id].caller_channel)
if (self.__previous_audio[call_id] != audioFile):
- self.calls[call_id].caller_ami.setVar(channel = call_id, variable = "TALK_AUDIO", value = audioFile)
+ self.calls[call_id].caller_ami.setVar(channel = self.calls[call_id].caller_channel, variable = "TALK_AUDIO", value = audioFile)
self.__previous_audio[call_id] = audioFile
"""
Redirect to the send sound file extension - note that we assume that we only have one channel to
Added: asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/confbridge.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/confbridge.conf?view=auto&rev=3213
==============================================================================
--- asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/confbridge.conf (added)
+++ asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/confbridge.conf Tue May 1 08:32:32 2012
@@ -1,0 +1,14 @@
+[general]
+
+[default_user]
+type = user
+marked = yes
+startmuted = no
+
+[default_bridge]
+type = bridge
+
+[bridge_record_file]
+type = bridge
+record_conference = yes
+record_file = confbridge_recording_bridge.wav
Propchange: asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/confbridge.conf
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/confbridge.conf
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/confbridge.conf
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/extensions.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/extensions.conf?view=auto&rev=3213
==============================================================================
--- asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/extensions.conf (added)
+++ asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/extensions.conf Tue May 1 08:32:32 2012
@@ -1,0 +1,69 @@
+[caller]
+
+exten => sendDTMF,1,NoOp()
+ same => n,Verbose(1, Sending DTMF Signal ${DTMF_TO_SEND})
+ same => n,SendDTMF(${DTMF_TO_SEND})
+ same => n,Goto(caller,wait,1)
+
+exten => sendAudio,1,NoOp()
+ same => n,Verbose(1, Sending audio file ${TALK_AUDIO})
+ same => n,Playback(${TALK_AUDIO})
+ same => n,Goto(caller,wait,1)
+
+exten => sendAudioWithDTMF,1,NoOp()
+ same => n,Verbose(1, Sending audio file ${TALK_AUDIO})
+ same => n,Playback(${TALK_AUDIO})
+ same => n,Verbose(1, Sending DTMF Signal ${DTMF_TO_SEND})
+ same => n,SendDTMF(${DTMF_TO_SEND})
+ same => n,Goto(caller,wait,1)
+
+exten => hangup,1,NoOp()
+ same => n,Verbose(1, Hanging up)
+ same => n,Hangup()
+
+exten => wait,1,NoOp()
+ same => n,Wait(10000)
+
+[confbridge]
+
+exten => record-default,1,NoOp()
+ same => n,Set(CONFBRIDGE(bridge,template)=default_bridge)
+ same => n,Set(CONFBRIDGE(bridge,record_conference)=yes)
+ same => n,ConfBridge(1)
+ same => n,Hangup()
+
+exten => record-conf,1,NoOp()
+ same => n,ConfBridge(1,bridge_record_file,default_user)
+ same => n,Hangup()
+
+exten => record-func,1,NoOp()
+ same => n,Set(CONFBRIDGE(bridge,template)=default_bridge)
+ same => n,Set(CONFBRIDGE(bridge,record_conference)=yes)
+ same => n,Set(CONFBRIDGE(bridge,record_file)=${RECORD_FILE})
+ same => n,ConfBridge(1)
+ same => n,Hangup()
+
+[talkdetect]
+
+exten => playback,1,Answer()
+ same => n,Playback(${TESTAUDIO})
+ same => n,Wait(2)
+ same => n,Hangup()
+
+exten => detect_audio,1,Answer()
+ same => n,Set(TALK_DETECTED=0) ; initialize TALK_DETECT var
+ same => n,BackgroundDetect(${TESTAUDIO},1,100,,20000)
+ same => n,GoToIf($[${TALK_DETECTED}=0]?talkdetectfail:talkdetectpass)
+ same => n(talkdetectfail),NoOp()
+ same => n,UserEvent(TestStatus, status: fail, message: failed to detect audio in ${TESTAUDIO})
+ same => n,Hangup()
+ same => n(talkdetectpass),NoOp()
+ same => n,UserEvent(TestStatus, status: pass, message: detected audio in ${TESTAUDIO})
+ same => n,Hangup()
+
+exten => talk,1,NoOp()
+ same => n,Verbose(Speech detected in ${TESTAUDIO})
+ same => n,UserEvent(TestStatus, status: pass, message: detected audio in ${TESTAUDIO})
+ same => n,Hangup()
+
+
Propchange: asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/extensions.conf
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/extensions.conf
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: asterisk/trunk/tests/apps/confbridge/confbridge_recording/configs/ast1/extensions.conf
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: asterisk/trunk/tests/apps/confbridge/confbridge_recording/run-test
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/apps/confbridge/confbridge_recording/run-test?view=auto&rev=3213
==============================================================================
--- asterisk/trunk/tests/apps/confbridge/confbridge_recording/run-test (added)
+++ asterisk/trunk/tests/apps/confbridge/confbridge_recording/run-test Tue May 1 08:32:32 2012
@@ -1,0 +1,342 @@
+#!/usr/bin/env python
+# vim: sw=3 et:
+'''
+Copyright (C) 2012, Digium, Inc.
+Matt Jordan <mjordan at digium.com>
+
+This program is free software, distributed under the terms of
+the GNU General Public License Version 2.
+'''
+
+import sys
+import os
+import logging
+
+from twisted.internet import reactor
+
+sys.path.append("lib/python")
+
+from asterisk.asterisk import Asterisk
+from asterisk.TestCase import TestCase
+from asterisk.TestState import TestStateController
+from asterisk.confbridge import ConfbridgeTestState
+from asterisk.confbridge import ConfbridgeChannelObject
+
+logger = logging.getLogger(__name__)
+
+
+class StartConfBridgeState(ConfbridgeTestState):
+ """
+ The initial state when users join the ConfBridge. This particular state
+ tracks the user through the initial ConfBridge login. It also sets in the
+ TestCase the MixMonitor filename being created. Once the user has joined
+ into the conference, the state transitions to the ActiveConfBridgeState.
+ """
+
+ def __init__(self, controller, testCase, calls, ami):
+ """
+ controller The TestStateController managing the test
+ testCase The main test object
+ calls A dictionary (keyed by conf_bridge_channel ID) of ConfbridgeChannelObjects
+ ami AMI instance
+ """
+ ConfbridgeTestState.__init__(self, controller, testCase, calls)
+
+ self._ami = ami
+ ami.registerEvent('ConfbridgeJoin', self.handle_confbridge_join_event)
+ ami.registerEvent('VarSet', self.handle_varset_event)
+
+ def handle_varset_event(self, ami, event):
+ """ VarSet event handler """
+
+ if 'variable' not in event or 'value' not in event:
+ return
+ if event['variable'] != 'MIXMONITOR_FILENAME':
+ return
+ logger.debug("Detected variable MIXMONITOR_FILENAME: %s" % event['value'])
+ self.testCase.set_recording_file(event['value'])
+
+ def handle_confbridge_join_event(self, ami, event):
+ """ ConfBridgeJoin event handler """
+
+ if 'channel' not in event:
+ return
+ if not self.calls:
+ logger.warning("No callers registered when channels joined")
+ return
+ if len(self.calls) > 1:
+ logger.warning("More then one channel in ConfBridge, using first")
+ self.conf_bridge_channel = self.calls.keys()[0]
+
+ if event['channel'] == self.conf_bridge_channel:
+ self.testCase.expected_events['confbridgejoined'] = True
+ # Transition to the next state
+ logger.debug("Joined ConfBridge, transitioning to next state")
+ self.changeState(ActiveConfBridgeState(self.controller, self.testCase, self.calls, self._ami))
+
+ def handleStateChange(self, ami, event):
+ """ TestEvent handler """
+
+ state = event['state']
+ if state == 'CONF_START_RECORD':
+ logger.debug("Detected CONF_START_RECORD")
+ self.testCase.expected_events['recordingstarted'] = True
+
+ def getStateName(self):
+ return "START"
+
+class ActiveConfBridgeState(ConfbridgeTestState):
+ """
+ State when the user is in the ConfBridge. A sound file containing some
+ voice is passed into the ConfBridge, then the user hangs up. This will
+ trigger the talk detection in the TestCase class. The state verifies
+ that the user leaves the conference and that the conference ends
+ appropriately.
+ """
+
+ def __init__(self, controller, testCase, calls, ami):
+ """
+ controller The TestStateController managing the test
+ testCase The main test object
+ calls A dictionary (keyed by conf_bridge_channel ID) of ConfbridgeChannelObjects
+ ami The instance of AMI to subscribe for events on
+ """
+ ConfbridgeTestState.__init__(self, controller, testCase, calls)
+ self.testCase.reset_timeout()
+ self._ami = ami
+ ami.registerEvent('ConfbridgeLeave', self.handle_confbridge_leave_event)
+ ami.registerEvent('ConfbridgeEnd', self.handle_confbridge_end_event)
+
+ if len(calls) != 1:
+ logger.warning("Multiple channels detected in ConfBridge, only the first will be used")
+ self._bridge_channel = calls.keys()[0]
+
+ # Schedule actions to take place
+ audio_file = os.path.join(os.getcwd(), "tests/apps/confbridge/sounds/talking")
+ self.scheduleSendSoundFile(1, self._bridge_channel, audio_file)
+ reactor.callLater(5, self.send_hangup)
+
+ def send_hangup(self):
+ self.hangup(self._bridge_channel)
+
+ def handleStateChange(self, ami, event):
+ """ TestEvent handler. Check for end of recording. """
+
+ state = event['state']
+ if state == 'CONF_STOP_RECORD':
+ logger.debug("Detected CONF_STOP_RECORD")
+ self.testCase.expected_events['recordingstopped'] = True
+
+ def handle_confbridge_leave_event(self, ami, event):
+ """ ConfBridgeLeave event handler """
+
+ if 'channel' not in event:
+ return
+ if event['channel'] != self._bridge_channel:
+ return
+ self.testCase.expected_events['confbridgeleave'] = True
+
+ def handle_confbridge_end_event(self, ami, event):
+ """ ConfBridgeEndEvent handler """
+
+ self.testCase.expected_events['confbridgeend'] = True
+
+ def getStateName(self):
+ return "ACTIVE"
+
+
+class ConfBridgeRecording(TestCase):
+ """
+ The TestCase class that executes the test. In each iteration of the test,
+ a local channel is created that is placed into a ConfBridge and activates
+ recording in some fashion. A TestStateController is used to manage the
+ actions of the channel in the ConfBridge.
+ """
+
+ def __init__(self):
+ super(ConfBridgeRecording, self).__init__()
+ self.create_asterisk()
+
+ # A dictionary is used to set the parameters for each test.
+ # This includes the channel to create, and, if applicable, the
+ # recording file to set for the ConfBridge.
+ self._tests = [{'channel':'local/record-default at confbridge',
+ 'file': ''},
+ {'channel': 'local/record-conf at confbridge',
+ 'file': ''},
+ {'channel': 'local/record-func at confbridge',
+ 'file': 'confbridge_recording_func.wav'},]
+ self._test_results = []
+ self._current_test = 0
+ self._controlling_channel = ''
+ self._confbridge_channel = ''
+ self._candidate_channels = []
+ self.expected_events = {}
+ self.record_file = ''
+
+ # Add the events we expect to see and receive
+ self.expected_events['confbridgejoined'] = False
+ self.expected_events['recordingstarted'] = False
+ self.expected_events['recordingstopped'] = False
+ self.expected_events['recordingfilename'] = False
+ self.expected_events['confbridgeleave'] = False
+ self.expected_events['confbridgeend'] = False
+
+ def confbridge_ended(self):
+ """ Called when the confbridge channels have hung up. This
+ causes the talk detection extension to be called to determine
+ if we've recorded anything, and if we received all expected
+ events from the test states.
+ """
+
+ # Check that we got all the expected events
+ failed_events = [e for e, v in self.expected_events.items() if not v]
+ if failed_events:
+ for event in failed_events:
+ logger.warning("Failed to detect %s" % event)
+ self._test_results.append(False)
+ else:
+ # Set to true for now; the talk detection result will set this to
+ # False if needed
+ self._test_results.append(True)
+
+ # Check the recorded file
+ logger.debug("Performing talk detection on file %s " % self._record_file[:len(self._record_file) - 4])
+ self.ami[0].originate(channel = "Local/detect_audio at talkdetect",
+ context = 'talkdetect', exten='playback', priority='1',
+ variable = {'TESTAUDIO': '"%s"' % (self._record_file[:len(self._record_file) - 4])}
+ ).addErrback(self.handleOriginateFailure)
+
+ def set_recording_file(self, filename):
+ """ Called by the test states when the recorded file is known """
+ self._record_file = filename
+ self.expected_events['recordingfilename'] = True
+
+ def ami_connect(self, ami):
+ super(ConfBridgeRecording, self).ami_connect(ami)
+
+ self.testStateController = TestStateController(self, ami)
+
+ ami.registerEvent('UserEvent', self.user_event_handler)
+ ami.registerEvent('Newchannel', self.new_channel_handler)
+ ami.registerEvent('Hangup', self.hangup_event_handler)
+ ami.registerEvent('Newexten', self.new_exten_event_handler)
+ self.originate_call(ami)
+
+ def _reset_objects(self, ami):
+ """ Reset objects for the next test execution """
+
+ self._confbridge_channel = ''
+ self._controlling_channel = ''
+ self.record_file = ''
+ self._candidate_channels = []
+ self._start_object = StartConfBridgeState(self.testStateController, self, {}, ami)
+ self.testStateController.changeState(self._start_object)
+ for e in self.expected_events:
+ self.expected_events[e] = False
+
+ def originate_call(self, ami):
+ """ Originate a new test """
+
+ self._reset_objects(ami)
+
+ channel = self._tests[self._current_test]['channel']
+ logger.debug("Originating call to %s" % channel)
+ variable = {}
+ if self._tests[self._current_test]['file']:
+ variable["RECORD_FILE"] = self._tests[self._current_test]['file']
+ ami.originate(channel = channel,
+ context = 'caller', exten='wait', priority='1',
+ variable = variable
+ ).addErrback(self.handleOriginateFailure)
+
+ def new_exten_event_handler(self, ami, event):
+ """ NewExten event handler. We use this to determine which
+ of the local channels is entering the ConfBridge, and which
+ is the 'controlling' channel """
+
+ if 'application' not in event or 'channel' not in event:
+ return
+
+ # We only care about the NewExten event that contains the ConfBridge
+ # application
+ if event['application'] != 'ConfBridge':
+ return
+
+ self._confbridge_channel = event['channel']
+
+ # Find the channel not in the ConfBridge
+ controlling_channels = [c for c in self._candidate_channels if c != self._confbridge_channel]
+ if (len(controlling_channels) != 1):
+ logger.warning("We only expected 1 controlling channel: %s" % str(controlling_channels))
+ return
+ self._controlling_channel = controlling_channels[0]
+
+ self._start_object.registerNewCaller(
+ ConfbridgeChannelObject(self._confbridge_channel, self._controlling_channel, ami))
+
+ def hangup_event_handler(self, ami, event):
+ """ Hangup event handler. Trigger the end of test logic when the confbridge
+ channel has hung up """
+
+ if 'channel' not in event:
+ return
+ if event['channel'] != self._confbridge_channel:
+ return
+ logger.debug("Hangup detected of ConfBridge channel %s" % self._confbridge_channel)
+ self.confbridge_ended()
+
+ def new_channel_handler(self, ami, event):
+ """ Record all new non-Bridge channels. This lets us later determine
+ which channel is in the ConfBridge, and which is the controlling channel
+ """
+
+ if 'channel' not in event:
+ return
+ if 'Bridge' in event['channel']:
+ # Disregard the ConfBridge Bridge channel
+ return
+ self._candidate_channels.append(event['channel'])
+
+ def user_event_handler(self, ami, event):
+ """ UserEvents are fired with the pass/fail status of the talk detection """
+
+ if event['userevent'] != 'TestStatus':
+ return
+ if 'status' not in event or 'message' not in event:
+ return
+ logger.debug("Received status %s: %s" % (event['status'], event['message']))
+ # Note that we only want to override the test results if it Failed
+ if event['status'] == 'fail':
+ logger.warning("Failed [%s] on test %d" % (event['message'], self._current_test))
+ self._test_results[self._current_test] = False
+ else:
+ logger.info("Passed [%s] in test %d" % (event['message'], self._current_test))
+
+ self._current_test += 1
+ if (self._current_test == len(self._tests)):
+ # Set the overall test status
+ self.passed = all(self._test_results)
+ logger.info("All tests executed, stopping reactor")
+ self.stop_reactor()
+ else:
+ logger.debug("Starting next test")
+ self.originate_call(ami)
+
+ def run(self):
+ super(ConfBridgeRecording, self).run()
+ self.create_ami_factory()
+
+def main():
+
+ test = ConfBridgeRecording()
+ reactor.run()
+
+ if not test.passed:
+ return 1
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main() or 0)
+
Propchange: asterisk/trunk/tests/apps/confbridge/confbridge_recording/run-test
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: asterisk/trunk/tests/apps/confbridge/confbridge_recording/run-test
------------------------------------------------------------------------------
svn:executable = *
Propchange: asterisk/trunk/tests/apps/confbridge/confbridge_recording/run-test
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: asterisk/trunk/tests/apps/confbridge/confbridge_recording/run-test
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: asterisk/trunk/tests/apps/confbridge/confbridge_recording/test-config.yaml
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/apps/confbridge/confbridge_recording/test-config.yaml?view=auto&rev=3213
==============================================================================
--- asterisk/trunk/tests/apps/confbridge/confbridge_recording/test-config.yaml (added)
+++ asterisk/trunk/tests/apps/confbridge/confbridge_recording/test-config.yaml Tue May 1 08:32:32 2012
@@ -1,0 +1,28 @@
+testinfo:
+ summary: 'Test conference recording in app_confbridge'
+ description: |
+ This tests recording conferences in ConfBridge. In each scenario, a
+ local channel is placed into a ConfBridge, and a sound file containing
+ a voice talking is played into the conference. The channel is then
+ hung up, and the expected sound file analyzed using the BackgroundDetect
+ application. This test checks three scenarios:
+ 1) Setting the record_conference bridge parameter using the CONFBRIDGE
+ function on a default bridge.
+ 2) Using a non-default bridge with the record_conference/record_file
+ parameters set.
+ 3) Setting the record_conference/record_file bridge parameters using
+ the CONFBRIDGE function on a default bridge.
+
+properties:
+ minversion: '10'
+ buildoption: 'TEST_FRAMEWORK'
+ tags:
+ - confbridge
+ - mixmonitor
+ - apps
+ dependencies:
+ - python : 'twisted'
+ - python : 'starpy'
+ - asterisk : 'app_confbridge'
+ - asterisk : 'app_playback'
+ - asterisk : 'app_talkdetect'
Propchange: asterisk/trunk/tests/apps/confbridge/confbridge_recording/test-config.yaml
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: asterisk/trunk/tests/apps/confbridge/confbridge_recording/test-config.yaml
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: asterisk/trunk/tests/apps/confbridge/confbridge_recording/test-config.yaml
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: asterisk/trunk/tests/apps/confbridge/tests.yaml
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/apps/confbridge/tests.yaml?view=diff&rev=3213&r1=3212&r2=3213
==============================================================================
--- asterisk/trunk/tests/apps/confbridge/tests.yaml (original)
+++ asterisk/trunk/tests/apps/confbridge/tests.yaml Tue May 1 08:32:32 2012
@@ -1,3 +1,4 @@
# Enter tests here in the order they should be considered for execution:
tests:
- test: 'confbridge_nominal'
+ - test: 'confbridge_recording'
More information about the svn-commits
mailing list