[asterisk-commits] dlee: branch dlee/ari-tests r3842 - in /asterisk/team/dlee/ari-tests: lib/pyt...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Tue Jun 11 15:48:02 CDT 2013
Author: dlee
Date: Tue Jun 11 15:48:01 2013
New Revision: 3842
URL: http://svnview.digium.com/svn/testsuite?view=rev&rev=3842
Log:
Test passes
Modified:
asterisk/team/dlee/ari-tests/lib/python/asterisk/TestRunner.py
asterisk/team/dlee/ari-tests/lib/python/asterisk/ari.py
asterisk/team/dlee/ari-tests/tests/rest_api/continue/configs/ast1/extensions.conf
asterisk/team/dlee/ari-tests/tests/rest_api/continue/rest_continue.py
asterisk/team/dlee/ari-tests/tests/rest_api/continue/test-config.yaml
Modified: asterisk/team/dlee/ari-tests/lib/python/asterisk/TestRunner.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/dlee/ari-tests/lib/python/asterisk/TestRunner.py?view=diff&rev=3842&r1=3841&r2=3842
==============================================================================
--- asterisk/team/dlee/ari-tests/lib/python/asterisk/TestRunner.py (original)
+++ asterisk/team/dlee/ari-tests/lib/python/asterisk/TestRunner.py Tue Jun 11 15:48:01 2013
@@ -48,7 +48,7 @@
for path in search_paths:
if os.path.exists('%s/%s.py' % (path, fullname)):
return TestModuleLoader(path)
- LOGGER.warn("Unable to find module '%s'" % fullname)
+ LOGGER.debug("Unable to find module '%s'" % fullname)
return None
class TestModuleLoader(object):
Modified: asterisk/team/dlee/ari-tests/lib/python/asterisk/ari.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/dlee/ari-tests/lib/python/asterisk/ari.py?view=diff&rev=3842&r1=3841&r2=3842
==============================================================================
--- asterisk/team/dlee/ari-tests/lib/python/asterisk/ari.py (original)
+++ asterisk/team/dlee/ari-tests/lib/python/asterisk/ari.py Tue Jun 11 15:48:01 2013
@@ -6,10 +6,18 @@
the GNU General Public License Version 2.
'''
+import datetime
+import json
import logging
+import re
+import requests
import time
+import traceback
import urllib
-import websocket
+
+from twisted.internet import reactor
+from autobahn.websocket import WebSocketClientFactory, \
+ WebSocketClientProtocol, connectWS
logger = logging.getLogger(__name__)
@@ -22,51 +30,186 @@
return self.min <= v <= self.max
def decode_range(v):
- if isinstance(v, int):
+ if v is None:
+ # Unspecified; recieve at least one
+ return Range(1, float("inf"))
+ elif isinstance(v, int):
+ # Need exactly this many events
return Range(v, v)
- elif count[0] == '<':
+ elif v[0] == '<':
# Need at most this many events
- self.count_min = 0
- self.count_max = int(count[1:])
- elif count[0] == '>':
+ return Range(0, int(v[1:]))
+ elif v[0] == '>':
# Need at least this many events
- self.count_min = int(count[1:])
- self.count_max = float("inf")
+ return Range(int(v[1:]), float("inf"))
else:
# Need exactly this many events
- self.count_min = int(count)
- self.count_max = int(count)
+ return Range(int(v), int(v))
class EventMatcher(object):
- def __init__(self, instance_config, test_object):
+ def __init__(self, ari, instance_config, test_object):
+ self.ari = ari
self.instance_config = instance_config
self.test_object = test_object
+ self.conditions = self.instance_config['conditions']
+ self.count_range = decode_range(self.instance_config.get('count'))
+ self.count = 0
+ self.passed = True
+ callback = self.instance_config.get('callback')
+ if callback:
+ module = __import__(callback['module'])
+ self.callback = getattr(module, callback['method'])
+ else:
+ # No callback; just use a no-op
+ self.callback = lambda **kwargs: None
+
+ test_object.register_stop_observer(self.on_stop)
+
+ def on_event(self, message):
+ if self.matches(message):
+ self.count += 1
+ # Split call and accumulation to always call the callback
+ try:
+ res = self.callback(self.ari, message)
+ if not res:
+ logger.error("Callback failed: %r" %
+ self.instance_config)
+ self.passed = self.passed and res
+ except:
+ logger.error("Exception in callback: %s" % traceback.format_exc())
+ self.passed = False
+
+ def on_stop(self, *args):
+ if not self.count_range.contains(self.count):
+ logger.error("Expected %d <= count <= %d; was %d (%r)",
+ self.count_range.min, self.count_range.max,
+ self.count, self.conditions)
+ self.passed = False
+ self.test_object.set_passed(self.passed)
+
+ def matches(self, message):
+ # Validate the match
+ match = self.conditions.get('match')
+ res = all_match(match, message)
+
+ # Now validate the nomatch, if it's there
+ nomatch = self.conditions.get('nomatch')
+ if res and nomatch:
+ res = not all_match(nomatch, message)
+ return res
+
+def all_match(pattern, message):
+ #logger.debug("%r ?= %r" % (pattern, message))
+ if pattern is None:
+ return True
+ elif isinstance(pattern, list):
+ res = len(pattern) == len(message)
+ i = 0
+ while res and i < len(pattern):
+ res = all_match(pattern[i], message[i])
+ return res
+ elif isinstance(pattern, dict):
+ for key, value in pattern.iteritems():
+ to_check = message.get(key)
+ if to_check is None or not all_match(value, to_check):
+ return False
+ return True
+ elif isinstance(pattern, str):
+ return re.match(pattern, message) is not None
+ elif isinstance(pattern, int):
+ return pattern == message
+ else:
+ logger.error("Unhandled pattern type %s" % type(pattern)).__name__
+
+
+class StasisClientProtocol(WebSocketClientProtocol):
+ def __init__(self, on_event):
+ self.on_event = on_event
+
+ def onOpen(self):
+ logger.debug("onOpen()")
+
+ def onClose(self, wasClean, code, reason):
+ logger.debug("onClose(%r, %d, %s)" % (wasClean, code, reason))
+ reactor.callLater(1, self.factory.reconnect)
+
+ def onMessage(self, msg, binary):
+ self.on_event(json.loads(msg))
+
+
+class StasisClientFactory(WebSocketClientFactory):
+ def __init__(self, host, port, apps, on_event, timeout_secs=15):
+ url = "ws://%s:%d/ws?%s" % \
+ (host, port, urllib.urlencode({'app': apps}))
+ WebSocketClientFactory.__init__(self, url, protocols=["stasis"])
+ self.on_event = on_event
+ self.timeout_secs = timeout_secs
+ self.protocol = self.__build_protocol
+ self.attempts = 0
+ self.start = None
+
+ self.reconnect()
+
+ def __build_protocol(self):
+ return StasisClientProtocol(self.on_event)
+
+ def clientConnectionFailed(self, connector, reason):
+ logger.info("clientConnectionFailed(%s)" % (reason))
+ reactor.callLater(1, self.reconnect)
+
+ def reconnect(self):
+ self.attempts += 1
+ logger.debug("WebSocket attempt #%d" % self.attempts)
+ if not self.start:
+ self.start = datetime.datetime.now()
+ runtime = (datetime.datetime.now() - self.start).seconds
+ if runtime >= self.timeout_secs:
+ logger.error(" Giving up after %d seconds" % self.timeout_secs)
+ else:
+ connectWS(self)
+
+
+class ARI(object):
+ def __init__(self, host, port):
+ self.base_url = "http://%s:%d/stasis" % (host, port)
+
+ def build_url(self, *args, **kwargs):
+ url = '/'.join([self.base_url] + list(args))
+ params = urllib.urlencode(kwargs)
+ return "%s?%s" % (url, params)
+
+ def get(self, *args, **kwargs):
+ url = self.build_url(*args, **kwargs)
+ logger.info("GET %s" % url)
+ return requests.get()
+
+ def post(self, *args, **kwargs):
+ url = self.build_url(*args, **kwargs)
+ logger.info("POST %s" % url)
+ return requests.post(url)
+
+ def delete(self, *args, **kwargs):
+ url = self.build_url(*args, **kwargs)
+ logger.info("DELETE %s" % url)
+ return requests.delete(url)
+
class WebSocketEventModule(object):
def __init__(self, module_config, test_object):
+ logger.info("WebSocketEventModule ctor")
+ self.host = '127.0.0.1'
+ self.port = 8088
self.test_object = test_object
-
- self.events = [
- EventMatcher(e, test_object) for e in module_config['events']]
-
+ self.ari = ARI(self.host, self.port)
+ self.event_matchers = [
+ EventMatcher(self.ari, e, test_object)
+ for e in module_config['events']]
apps = module_config['apps']
if isinstance(apps, list):
apps = ','.join(apps)
- path = "ws://127.0.0.1:8088/ws"
- params = urllib.urlencode({'app': apps})
- url = "%s?%s" % (path, params)
-
- tries = 1
- while True:
- try:
- self.ws = websocket.create_connection(
- url, header=["Sec-WebSocket-Protocol: stasis"])
- except:
- if tries > 10:
- logger.error("WebSocket connection failed.")
- break
- # Wait a bit and try again
- logger.info("WebSocket connection failed. Retrying...")
- tries += 1
- time.sleep(1)
-
+ self.factory = StasisClientFactory(host=self.host, port=8088, apps=apps,
+ on_event=self.on_event)
+
+ def on_event(self, event):
+ for matcher in self.event_matchers:
+ matcher.on_event(event)
Modified: asterisk/team/dlee/ari-tests/tests/rest_api/continue/configs/ast1/extensions.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/dlee/ari-tests/tests/rest_api/continue/configs/ast1/extensions.conf?view=diff&rev=3842&r1=3841&r2=3842
==============================================================================
--- asterisk/team/dlee/ari-tests/tests/rest_api/continue/configs/ast1/extensions.conf (original)
+++ asterisk/team/dlee/ari-tests/tests/rest_api/continue/configs/ast1/extensions.conf Tue Jun 11 15:48:01 2013
@@ -2,6 +2,6 @@
exten => s,1,NoOp()
same => n,Answer()
-# same => n,Stasis(continue-test)
+ same => n,Stasis(continue-test)
same => n,UserEvent(TestResult,Result: pass,Status: successfully broke out of Stasis)
same => n,Hangup()
Modified: asterisk/team/dlee/ari-tests/tests/rest_api/continue/rest_continue.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/dlee/ari-tests/tests/rest_api/continue/rest_continue.py?view=diff&rev=3842&r1=3841&r2=3842
==============================================================================
--- asterisk/team/dlee/ari-tests/tests/rest_api/continue/rest_continue.py (original)
+++ asterisk/team/dlee/ari-tests/tests/rest_api/continue/rest_continue.py Tue Jun 11 15:48:01 2013
@@ -8,16 +8,19 @@
import logging
-LOGGER = logging.getLogger(__name__)
+logger = logging.getLogger(__name__)
id = None
def on_start(ari, event):
+ logger.debug("rest_continue.on_start(%r)" % event)
global id
- id = event['channel']['uniqueid']
- ari.resource('channels', id, 'continue').post
+ id = event['stasis_start']['channel']['uniqueid']
+ resp = ari.post('channels', id, 'continue')
+ resp.raise_for_status()
return True
def on_end(ari, event):
+ logger.debug("rest_continue.on_end(%r)" % event)
global id
- return id == event['channel']['uniqueid']
+ return id == event['stasis_end']['channel']['uniqueid']
Modified: asterisk/team/dlee/ari-tests/tests/rest_api/continue/test-config.yaml
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/dlee/ari-tests/tests/rest_api/continue/test-config.yaml?view=diff&rev=3842&r1=3841&r2=3842
==============================================================================
--- asterisk/team/dlee/ari-tests/tests/rest_api/continue/test-config.yaml (original)
+++ asterisk/team/dlee/ari-tests/tests/rest_api/continue/test-config.yaml Tue Jun 11 15:48:01 2013
@@ -24,17 +24,19 @@
ari-config:
apps: continue-test
events:
- - match:
- application: continue-test
- stasis_start:
- args: []
+ - conditions:
+ match:
+ application: continue-test
+ stasis_start:
+ args: []
count: 1
callback:
module: rest_continue
method: on_start
- - match:
- application: continue-test
- stasis_end:
+ - conditions:
+ match:
+ application: continue-test
+ stasis_end:
count: 1
callback:
module: rest_continue
@@ -53,7 +55,7 @@
properties:
minversion: '12.0.0'
dependencies:
- - python : websocket
+ - python : autobahn.websocket
- python : requests
- python : twisted
- python : starpy
More information about the asterisk-commits
mailing list