[asterisk-commits] mmichelson: testsuite/asterisk/trunk r5145 - in /asterisk/trunk: lib/python/a...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Thu Jun 19 16:34:56 CDT 2014
Author: mmichelson
Date: Thu Jun 19 16:34:48 2014
New Revision: 5145
URL: http://svnview.digium.com/svn/testsuite?view=rev&rev=5145
Log:
Add realtime pluggable test module and baseline realtime tests.
Review: https://reviewboard.asterisk.org/r/3363
Added:
asterisk/trunk/lib/python/asterisk/realtime_test_module.py (with props)
asterisk/trunk/tests/realtime/
asterisk/trunk/tests/realtime/destroy/
asterisk/trunk/tests/realtime/destroy/configs/
asterisk/trunk/tests/realtime/destroy/configs/ast1/
asterisk/trunk/tests/realtime/destroy/configs/ast1/extconfig.conf (with props)
asterisk/trunk/tests/realtime/destroy/configs/ast1/sorcery.conf (with props)
asterisk/trunk/tests/realtime/destroy/destroy.py (with props)
asterisk/trunk/tests/realtime/destroy/test-config.yaml (with props)
asterisk/trunk/tests/realtime/multi/
asterisk/trunk/tests/realtime/multi/configs/
asterisk/trunk/tests/realtime/multi/configs/ast1/
asterisk/trunk/tests/realtime/multi/configs/ast1/extconfig.conf (with props)
asterisk/trunk/tests/realtime/multi/configs/ast1/sorcery.conf (with props)
asterisk/trunk/tests/realtime/multi/multi.py (with props)
asterisk/trunk/tests/realtime/multi/test-config.yaml (with props)
asterisk/trunk/tests/realtime/single/
asterisk/trunk/tests/realtime/single/configs/
asterisk/trunk/tests/realtime/single/configs/ast1/
asterisk/trunk/tests/realtime/single/configs/ast1/extconfig.conf (with props)
asterisk/trunk/tests/realtime/single/configs/ast1/sorcery.conf (with props)
asterisk/trunk/tests/realtime/single/single.py (with props)
asterisk/trunk/tests/realtime/single/test-config.yaml (with props)
asterisk/trunk/tests/realtime/static/
asterisk/trunk/tests/realtime/static/configs/
asterisk/trunk/tests/realtime/static/configs/ast1/
asterisk/trunk/tests/realtime/static/configs/ast1/extconfig.conf (with props)
asterisk/trunk/tests/realtime/static/configs/ast1/modules.conf (with props)
asterisk/trunk/tests/realtime/static/static.py (with props)
asterisk/trunk/tests/realtime/static/test-config.yaml (with props)
asterisk/trunk/tests/realtime/store/
asterisk/trunk/tests/realtime/store/configs/
asterisk/trunk/tests/realtime/store/configs/ast1/
asterisk/trunk/tests/realtime/store/configs/ast1/extconfig.conf (with props)
asterisk/trunk/tests/realtime/store/configs/ast1/sorcery.conf (with props)
asterisk/trunk/tests/realtime/store/store.py (with props)
asterisk/trunk/tests/realtime/store/test-config.yaml (with props)
asterisk/trunk/tests/realtime/tests.yaml (with props)
asterisk/trunk/tests/realtime/update/
asterisk/trunk/tests/realtime/update/configs/
asterisk/trunk/tests/realtime/update/configs/ast1/
asterisk/trunk/tests/realtime/update/configs/ast1/extconfig.conf (with props)
asterisk/trunk/tests/realtime/update/configs/ast1/sorcery.conf (with props)
asterisk/trunk/tests/realtime/update/test-config.yaml (with props)
asterisk/trunk/tests/realtime/update/update.py (with props)
Modified:
asterisk/trunk/tests/tests.yaml
Added: asterisk/trunk/lib/python/asterisk/realtime_test_module.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/realtime_test_module.py?view=auto&rev=5145
==============================================================================
--- asterisk/trunk/lib/python/asterisk/realtime_test_module.py (added)
+++ asterisk/trunk/lib/python/asterisk/realtime_test_module.py Thu Jun 19 16:34:48 2014
@@ -1,0 +1,511 @@
+#!/usr/bin/env python
+"""
+Copyright (C) 2014, Digium, Inc.
+Mark Michelson <mmichelson at digium.com>
+
+This program is free software, distributed under the terms of
+the GNU General Public License Version 2.
+"""
+import logging
+import sys
+import cgi
+import re
+
+from twisted.internet import reactor
+from twisted.internet import error
+from twisted.web.server import Site
+from twisted.web.resource import Resource, NoResource
+
+LOGGER = logging.getLogger(__name__)
+
+THIS_MODULE = sys.modules[__name__]
+
+
+class RealtimeData(object):
+ """This class holds all of the data that is being stored in realtime. The
+ class cotains a dictionary of "tables" at the top level keyed on the
+ table's name. Each table consists of "rows" represented by a list of
+ dictionaries.
+
+ All variables are stored as strings since that is how they get passed in by
+ Asterisk and that is the format in which they are returned to Asterisk.
+ This means that type-checking on this sort of realtime backend is non-
+ existent, but since this is only being used in a test environment, I don't
+ care.
+ """
+ def __init__(self):
+ self.tables = {}
+
+ def add_rows(self, table_name, rows):
+ """Add multiple rows to a table.
+
+ :param table_name: String representing table to update.
+ :param rows: List of dictionaries to add to the table.
+ """
+ try:
+ self.tables[table_name].extend(rows)
+ except KeyError:
+ self.tables[table_name] = rows
+
+ def add_row(self, table_name, row):
+ """Add a single row to a table.
+
+ :param table_name: String representing table to update.
+ :param row: Dictionary to add to the table.
+ """
+ self.add_rows(table_name, [row])
+
+ def retrieve_row(self, table_name, where):
+ """Retrieve a single row from a table.
+
+ :param table_name: String representing table to update.
+ :param where: Dictionary used to determine which row to retrieve.
+
+ :returns: Row which matches the given input. If multiple rows match,
+ then only the first is returned. If no rows match, an empty row is
+ returned.
+
+ :raises: KeyError if a table does not exist with name table_name.
+ """
+ rows = self.retrieve_rows(table_name, where)
+ if rows:
+ LOGGER.debug("Retrieved rows %s" % rows)
+ return rows[0]
+ else:
+ return {}
+
+ def _filter_rows(self, table, where):
+ """Internal method used to determine rows affected by an operation
+
+ :param table: The table on which to apply the where clause (not the
+ table name).
+ :param where: A dictionary of key/value pairs used to filter rows from
+ the table.
+ :returns: A list of rows that match the given input.
+
+ If a value is empty in the where clause, then it matches all rows in
+ the table that have the specified key.
+ """
+ LOGGER.debug("Table has %s" % table)
+ LOGGER.debug("Searching table for %s" % where)
+
+ return [row for row in table
+ if all(key in row and re.match(value, row[key])
+ for key, value in where.iteritems())]
+
+ def retrieve_rows(self, table_name, where):
+ """Retrieve multiple rows from a table.
+
+ :param table_name: String representing table to update.
+ :param where: Dictionary that determines which rows to retrieve.
+ :returns: List of rows that match the given input.
+ :raises: Keyerror if a table does not exist with name table_name
+ """
+ return self._filter_rows(self.tables[table_name], where)
+
+ def update_rows(self, table_name, where, update):
+ """Update row data in a table.
+
+ :param table_name: String representing table to update.
+ :param where: Dictionary that determines which rows to update.
+ :param update: Dictionary with new values to apply to affected rows.
+ :returns: number of rows that have been updated.
+ :raises: KeyError if a table does not exist with name table_name.
+ """
+ to_update = self._filter_rows(self.tables[table_name], where)
+
+ for item in to_update:
+ item.update(update)
+
+ LOGGER.debug("After update, table has %s" % self.tables[table_name])
+
+ return len(to_update)
+
+ def delete_rows(self, table_name, where):
+ """Delete rows from a table.
+ :param table_name: String representing table to delete from.
+ :param where: Dictionary that determines which rows to delete.
+ :returns: number of rows deleted.
+ :raises: KeyError if a table does not exist with name table_name.
+ """
+ LOGGER.debug("Being told to remove where %s" % where)
+ to_delete = self._filter_rows(self.tables[table_name], where)
+ LOGGER.debug("Items to delete are %s" % to_delete)
+ self.tables[table_name] = [row for row in self.tables[table_name]
+ if row not in to_delete]
+ return len(to_delete)
+
+
+class RootResource(Resource):
+ """Resource provided by the root of our HTTP server.
+ """
+ def __init__(self, rt_data):
+ Resource.__init__(self)
+ self.rt_data = rt_data
+ LOGGER.debug("Creating RootResource")
+
+ def getChild(self, name, request):
+ """Get child resource for site root.
+ :param name: Name of requested child resource.
+ :param request: incoming HTTP request.
+ :returns: Table resource for the given name.
+
+ When Asterisk makes an HTTP request, Twisted will initially call into
+ this resource in order to be given the proper resource given the path
+ in the URL. The name requested is expected to be one of the realtime
+ tables, so we create a TableResource representing that table. The
+ TableResource is then called into to get the resource relating to the
+ operation to perform on the table.
+ """
+ LOGGER.debug("Asking root for child %s", name)
+ return TableResource(name, self.rt_data)
+
+
+class TableResource(Resource):
+ """Resource for a specific table on our HTTP server.
+ """
+ def __init__(self, table_name, rt_data):
+ Resource.__init__(self)
+ self.table_name = table_name
+ self.rt_data = rt_data
+ LOGGER.debug("Creating TableResource for %s", table_name)
+
+ def getChild(self, name, request):
+ """Get child resource for a table
+ :param name: Name of requested child resource.
+ :param request: Incoming HTTP request.
+ :returns: LeafResource subclass.
+
+ Twisted automatically calls this when trying to get a requested
+ resource. The expected names of the resources are one of
+
+ * single
+ * multi
+ * update
+ * store
+ * destroy
+ * require
+ * static
+
+ Each of these corresponds to a resource class in this module. We use
+ reflection here to generate the requested resource dynamically. Each of
+ these resources represents an operation to perform on the table.
+ """
+ try:
+ return getattr(THIS_MODULE, "_" + name + "Resource")(
+ self.table_name, self.rt_data
+ )
+ except AttributeError as ex:
+ msg = "Error retrieving resource %s/%s: %s" % \
+ (self.table_name, name, ex)
+ LOGGER.error(msg)
+ return NoResource(message=msg)
+
+
+class LeafResource(Resource):
+ """Base class for leaf resources.
+
+ A leaf resource is one that has the isLeaf property set. This means that
+ there are no children for this resource in the hierarchy.
+
+ All resources representing table operations are subclasses of this
+ LeafResource since they all have no child resources. The LeafResource class
+ provides common operations that its children require, mostly pertaining to
+ manipulation of request data.
+ """
+ isLeaf = True
+
+ def __init__(self, table_name, rt_data):
+ Resource.__init__(self)
+ self.table_name = table_name
+ self.rt_data = rt_data
+ LOGGER.debug("Constructing LeafResource")
+
+ def unpack_args(self, args):
+ """Unpack lists used a args values.
+ :param args: Dictionary of key-list pairs to unpack
+ :returns: Same dictionary with values unpacked
+
+ A request object presents the URL and POST parameters as a dictionary
+ of the form {'param_name': [values]}. The values are placed in a list
+ to accommodate repeated parameter names that may appear in requests.
+ Since Asterisk does not repeat parameters, we are safe to unpack the
+ list.
+
+ Example input: {'foo': ['cat'], 'bar': ['dog']}
+ Example output: {'foo': 'cat', 'bar': 'dog'}
+
+ This method would be more efficient if we were using Python 2.7+
+ since we could use a dict comprehension.
+ """
+ filtered_args = {}
+ for key, values in args.iteritems():
+ if " LIKE" in key:
+ # Strip away " LIKE" and % from values
+ filtered_args[key[:-5]] = [val.replace('%', '.*')
+ for val in values]
+ else:
+ filtered_args[key] = values
+
+ LOGGER.debug('filtered args is %s' % filtered_args)
+
+ return dict((key, values[0] if values else '.*') for key, values in
+ filtered_args.iteritems())
+
+ def encode_row(self, row):
+ """Encode a retrieved row for an HTTP response.
+ :param row: Dictionary to format for HTTP response body.
+ :returns: String of key=values.
+
+ Example input: {'foo': 'cat, 'bar': 'dog', 'baz: 'donkey'}
+ Example output: 'foo=cat&bar=dog&baz=donkey'
+ """
+ string = '&'.join(['{0}={1}'.format(cgi.escape(key), cgi.escape(val))
+ for key, val in row.iteritems()])
+ LOGGER.debug("Returning response %s" % string)
+ return string
+
+ def encode_multi_row(self, rows):
+ """Encode multiple rows for an HTTP response.
+ :param rows: List of dictionaries to encode in HTTP response body.
+ :returns: Encoded rows, each row separated by a carriage return line
+ feed.
+ """
+ string = '\r\n'.join([self.encode_row(row) for row in rows])
+ LOGGER.debug("Returning response %s" % string)
+ return string
+
+ def return_404(self, request):
+ """Return a 404 HTTP response.
+ :param request: The request to which we are responding.
+ :returns: NoResource's rendering of the request.
+
+ Performed if a request tries to access a nonexistent table.
+ """
+ page = NoResource(message="Table %s could not be found" %
+ self.table_name)
+ return page.render(request)
+
+
+class _singleResource(LeafResource):
+ """Resource for retrieving single record.
+
+ This retrieves a single row of data from the
+ realtime data.
+ """
+ def render_POST(self, request):
+ """Handle the POST method.
+ :param request: HTTP POST request.
+ :returns: Single encoded row of data.
+
+ POST parameters are used to determine which row to retrieve from the
+ database.
+ """
+ LOGGER.debug("Asked to render_POST in the single resource")
+ LOGGER.debug("request.args is %s" % request.args)
+ try:
+ return self.encode_row(self.rt_data.retrieve_row(self.table_name,
+ self.unpack_args(request.args)))
+ except KeyError:
+ return self.return_404(request)
+
+
+class _multiResource(LeafResource):
+ """Resource for retrieving multiple records.
+
+ This retrieves multiple rows of data from the realtime data.
+ """
+ def render_POST(self, request):
+ """Handle the POST method.
+ :param request: HTTP POST request.
+ :returns: Multiple encoded rows.
+
+ POST parameters are used to determine which rows to retrieve from the
+ realtime data.
+
+ This is an abstraction where Asterisk does a poor job of being
+ amenable to non-database sources. Sending parameters like
+ "id LIKE=%" means we have some work cut out for us in order to be able
+ to successfully retrieve values.
+ """
+ # If this were python 2.7+ we could use a dict comprehension here
+ LOGGER.debug("Asked to render POST on the multi resource")
+ LOGGER.debug("Multi URI: %s" % request.uri)
+ try:
+ return self.encode_multi_row(
+ self.rt_data.retrieve_rows(self.table_name,
+ self.unpack_args(request.args)))
+ except KeyError:
+ return self.return_404(request)
+
+
+class _updateResource(LeafResource):
+ """Resource for updating records.
+ """
+ def render_POST(self, request):
+ """Handle the POST method.
+ :param request: HTTP POST request.
+ :returns: The number of rows updated by the request.
+
+ The URL parameters determine which objects to update, and the POST
+ parameters determine which object fields to update.
+
+ Twisted makes this a bit difficult since it combines the URL and POST
+ parameters into a single dictionary. So what we have to do is parse the
+ URL parameters out of the URL and then remove those from the dictionary
+ that Twisted gives us. With the parameters separated out, the update
+ operation can proceed.
+ """
+ LOGGER.debug("Asked to render POST in the update resource")
+
+ _, _, uri_params = request.uri.rpartition('?')
+ # The odd slice notation gets the first and third items from the tuple
+ # returned from item.partition
+ uri_args = dict(item.partition('=')[::2] for item in
+ uri_params.split('&'))
+
+ # The dict() created as the argument to unpack args is essentially the
+ # set difference between request.args and uri_args.
+ post_args = self.unpack_args(dict((key, request.args[key])
+ for key in request.args
+ if key not in uri_args))
+
+ LOGGER.debug("URI args: %s" % uri_args)
+ LOGGER.debug("POST args: %s" % post_args)
+ try:
+ affected = self.rt_data.update_rows(self.table_name, uri_args,
+ post_args)
+ except KeyError:
+ return self.return_404(request)
+ else:
+ return str(affected)
+
+
+class _storeResource(LeafResource):
+ """Resource for handling storing into realtime
+ """
+ def render_POST(self, request):
+ """Handle HTTP POST.
+ :param request: Incoming HTTP POST.
+ :returns: "1"
+
+ This is supposed to return the number of rows inserted, but since this
+ is only ever used to insert a single row, and insertion never fails,
+ this always returns "1".
+ """
+ LOGGER.debug("Asked to render POST in the store resource")
+ self.rt_data.add_row(
+ self.table_name, self.unpack_args(request.args)
+ )
+ return "1"
+
+
+class _destroyResource(LeafResource):
+ """Resource for handling destroying realtime data
+ """
+ def render_POST(self, request):
+ """Handle HTTP POST.
+ :param request: Incoming HTTP POST.
+ :returns: the number rows deleted.
+ """
+ LOGGER.debug("Asked to render POST in the destroy resource")
+ try:
+ affected = self.rt_data.delete_rows(
+ self.table_name, self.unpack_args(request.args)
+ )
+ except KeyError:
+ return self.return_404(request)
+ else:
+ return str(affected)
+
+
+class _requireResource(LeafResource):
+ """Resource for handling realtime requirements
+ """
+ def render_GET(self, request):
+ """Handle HTTP GET.
+ :param request: Incoming HTTP GET.
+ :returns: "0"
+
+ This is used to determine if the types being used for specific
+ realtime fields are correct. Since we store everything as a string, we
+ don't really care and rely on user input to store types that make sense
+ for the given tables. Returning a "0" body indicates we satisfy all
+ requirements.
+ """
+ return "0"
+
+
+class _staticResource(LeafResource):
+ """Resource used for static realtime.
+ """
+ def render_GET(self, request):
+ """Handle HTTP GET.
+ :param request: Incoming HTTP GET.
+ :returns: All matching records
+
+ Filename is passed in as URL parameter.
+ """
+ LOGGER.debug("Asked to render GET in the static resource")
+ # Forego a more complicated unpack since the file is the only thing we
+ # care about
+ where = {'filename': request.args['file'][0], 'commented': '0'}
+ try:
+ return self.encode_multi_row(
+ self.rt_data.retrieve_rows(self.table_name, where)
+ )
+ except KeyError:
+ return self.return_404(request)
+
+
+class RealtimeTestModule(object):
+ """Test module for realtime database tests.
+
+ This module uses an HTTP server to listen for requests in order to retrieve
+ or modify stored data. The actual data being retrieved is stored in an
+ instance of the RealtimeData class.
+ """
+ def __init__(self, module_config, test_object):
+ self.test_object = test_object
+ self.module_config = module_config
+ self.rt_data = RealtimeData()
+ self.test_object.register_ami_observer(self._ami_connect)
+
+ self.populate_rt_data(module_config.get('data'))
+ self.setup_http()
+
+ def populate_rt_data(self, data):
+ """Populate the database with an initial set of data.
+ :data: yaml dictionary that mimicks the RealtimeData format
+ """
+ if not data:
+ return
+
+ for table_name, rows in data.iteritems():
+ self.rt_data.add_rows(table_name, rows)
+
+ def setup_http(self):
+ """Create Twisted HTTP server.
+
+ We supply Twisted with a root resource to call into for all HTTP
+ requests made. The root resource is then responsible for dynamically
+ creating resources to handle specific requests
+ """
+ resource = RootResource(self.rt_data)
+ factory = Site(resource)
+ reactor.listenTCP(46821, factory)
+
+ def _ami_connect(self, ami):
+ """Callback for when AMI connects.
+
+ This is the launching point for the actual meat of each test. This
+ calls into a configuration-specified method, supplying the realtime
+ data.
+ """
+ if 'entry-method' not in self.module_config or \
+ 'entry_module' not in self.module_config:
+ return
+
+ module = __import__(self.module_config['entry_module'])
+ method = getattr(module, self.module_config['entry_method'])
+ method(self.rt_data, self.test_object, ami)
Propchange: asterisk/trunk/lib/python/asterisk/realtime_test_module.py
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: asterisk/trunk/lib/python/asterisk/realtime_test_module.py
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: asterisk/trunk/lib/python/asterisk/realtime_test_module.py
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: asterisk/trunk/tests/realtime/destroy/configs/ast1/extconfig.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/realtime/destroy/configs/ast1/extconfig.conf?view=auto&rev=5145
==============================================================================
--- asterisk/trunk/tests/realtime/destroy/configs/ast1/extconfig.conf (added)
+++ asterisk/trunk/tests/realtime/destroy/configs/ast1/extconfig.conf Thu Jun 19 16:34:48 2014
@@ -1,0 +1,2 @@
+[settings]
+mailboxes => curl,http://localhost:8000/mwi
Propchange: asterisk/trunk/tests/realtime/destroy/configs/ast1/extconfig.conf
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: asterisk/trunk/tests/realtime/destroy/configs/ast1/extconfig.conf
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: asterisk/trunk/tests/realtime/destroy/configs/ast1/extconfig.conf
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: asterisk/trunk/tests/realtime/destroy/configs/ast1/sorcery.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/realtime/destroy/configs/ast1/sorcery.conf?view=auto&rev=5145
==============================================================================
--- asterisk/trunk/tests/realtime/destroy/configs/ast1/sorcery.conf (added)
+++ asterisk/trunk/tests/realtime/destroy/configs/ast1/sorcery.conf Thu Jun 19 16:34:48 2014
@@ -1,0 +1,2 @@
+[res_mwi_external]
+mailboxes=realtime,mailboxes
Propchange: asterisk/trunk/tests/realtime/destroy/configs/ast1/sorcery.conf
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: asterisk/trunk/tests/realtime/destroy/configs/ast1/sorcery.conf
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: asterisk/trunk/tests/realtime/destroy/configs/ast1/sorcery.conf
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: asterisk/trunk/tests/realtime/destroy/destroy.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/realtime/destroy/destroy.py?view=auto&rev=5145
==============================================================================
--- asterisk/trunk/tests/realtime/destroy/destroy.py (added)
+++ asterisk/trunk/tests/realtime/destroy/destroy.py Thu Jun 19 16:34:48 2014
@@ -1,0 +1,45 @@
+#!/usr/bin/env python
+
+import logging
+
+LOGGER = logging.getLogger(__name__)
+
+
+class DestroyTest(object):
+ def __init__(self, rt_data, test_object, ami):
+ self.rt_data = rt_data
+ self.test_object = test_object
+ self.ami = ami
+
+ def fail_test(self):
+ self.test_object.set_passed(False)
+ self.test_object.stop_reactor()
+
+ def pass_test(self):
+ self.test_object.set_passed(True)
+ self.test_object.stop_reactor()
+
+ def mwi_callback(self, ami, event):
+ LOGGER.debug("Received MWI event %s" % event)
+
+ event_mailbox = event.get('mailbox')
+
+ query = dict(id=event_mailbox)
+ rt_row = self.rt_data.retrieve_row('mwi', query)
+ if rt_row:
+ LOGGER.error("Mailbox %s in AMI event still exists in realtime" %
+ event_mailbox)
+ self.fail_test()
+
+ self.pass_test()
+
+
+def check_it(rt_data, test_object, ami):
+ test = DestroyTest(rt_data, test_object, ami)
+
+ ami.registerEvent('MessageWaiting', test.mwi_callback)
+ message = {
+ 'Action': 'MWIDelete',
+ 'Mailbox': 'Dazed',
+ }
+ ami.sendMessage(message)
Propchange: asterisk/trunk/tests/realtime/destroy/destroy.py
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: asterisk/trunk/tests/realtime/destroy/destroy.py
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: asterisk/trunk/tests/realtime/destroy/destroy.py
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: asterisk/trunk/tests/realtime/destroy/test-config.yaml
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/realtime/destroy/test-config.yaml?view=auto&rev=5145
==============================================================================
--- asterisk/trunk/tests/realtime/destroy/test-config.yaml (added)
+++ asterisk/trunk/tests/realtime/destroy/test-config.yaml Thu Jun 19 16:34:48 2014
@@ -1,0 +1,43 @@
+testinfo:
+ summary: 'Realtime destroy test'
+ description: |
+ 'This tests the ability of Asterisk to destroy an single entry from realtime.
+ An MWI mailbox is preloaded in realtime, and Asterisk is tasked with
+ deleting the mailbox when asked.'
+
+
+properties:
+ minversion: '12.0.0'
+ dependencies:
+ - python: 'twisted'
+ - python: 'starpy'
+ - asterisk: 'res_mwi_external'
+ - asterisk: 'res_mwi_external_ami'
+ - asterisk: 'res_config_curl'
+ - asterisk: 'func_curl'
+ tags:
+ - realtime
+ - mwi_external
+
+test-modules:
+ add-test-to-search-path: 'True'
+ test-object:
+ config-section: test-case-config
+ typename: 'test_case.TestCaseModule'
+ modules:
+ -
+ config-section: 'realtime-config'
+ typename: 'realtime_test_module.RealtimeTestModule'
+
+test-case-config:
+ connect-ami: 'True'
+
+realtime-config:
+ entry_module: 'destroy'
+ entry_method: 'check_it'
+ data:
+ mwi:
+ -
+ id: 'Dazed'
+ msgs_new: '5'
+ msgs_old: '4'
Propchange: asterisk/trunk/tests/realtime/destroy/test-config.yaml
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: asterisk/trunk/tests/realtime/destroy/test-config.yaml
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: asterisk/trunk/tests/realtime/destroy/test-config.yaml
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: asterisk/trunk/tests/realtime/multi/configs/ast1/extconfig.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/realtime/multi/configs/ast1/extconfig.conf?view=auto&rev=5145
==============================================================================
--- asterisk/trunk/tests/realtime/multi/configs/ast1/extconfig.conf (added)
+++ asterisk/trunk/tests/realtime/multi/configs/ast1/extconfig.conf Thu Jun 19 16:34:48 2014
@@ -1,0 +1,2 @@
+[settings]
+mailboxes => curl,http://localhost:8000/mwi
Propchange: asterisk/trunk/tests/realtime/multi/configs/ast1/extconfig.conf
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: asterisk/trunk/tests/realtime/multi/configs/ast1/extconfig.conf
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: asterisk/trunk/tests/realtime/multi/configs/ast1/extconfig.conf
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: asterisk/trunk/tests/realtime/multi/configs/ast1/sorcery.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/realtime/multi/configs/ast1/sorcery.conf?view=auto&rev=5145
==============================================================================
--- asterisk/trunk/tests/realtime/multi/configs/ast1/sorcery.conf (added)
+++ asterisk/trunk/tests/realtime/multi/configs/ast1/sorcery.conf Thu Jun 19 16:34:48 2014
@@ -1,0 +1,2 @@
+[res_mwi_external]
+mailboxes=realtime,mailboxes
Propchange: asterisk/trunk/tests/realtime/multi/configs/ast1/sorcery.conf
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: asterisk/trunk/tests/realtime/multi/configs/ast1/sorcery.conf
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: asterisk/trunk/tests/realtime/multi/configs/ast1/sorcery.conf
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: asterisk/trunk/tests/realtime/multi/multi.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/realtime/multi/multi.py?view=auto&rev=5145
==============================================================================
--- asterisk/trunk/tests/realtime/multi/multi.py (added)
+++ asterisk/trunk/tests/realtime/multi/multi.py Thu Jun 19 16:34:48 2014
@@ -1,0 +1,82 @@
+#!/usr/bin/env python
+
+import logging
+
+LOGGER = logging.getLogger(__name__)
+
+EXPECTED_MAILBOXES = 2
+# Values from test-config.yaml
+MAILBOXES = [
+ {'mailbox': 'Dazed', 'new': '5', 'old': '4'},
+ {'mailbox': 'Confused', 'new': '6', 'old': '8'}
+]
+
+
+class MultiTest(object):
+ def __init__(self, rt_data, test_object, ami):
+ self.rt_data = rt_data
+ self.test_object = test_object
+ self.ami = ami
+ self.retrieved_mailboxes = 0
+
+ def fail_test(self):
+ self.test_object.set_passed(False)
+ self.test_object.stop_reactor()
+
+ def pass_test(self):
+ self.test_object.set_passed(True)
+ self.test_object.stop_reactor()
+
+ def mwi_get_callback(self, ami, event):
+ LOGGER.debug("Received MWI event %s" % event)
+ self.retrieved_mailboxes += 1
+
+ event_mailbox = event.get('mailbox')
+ event_new = event.get('newmessages')
+ event_old = event.get('oldmessages')
+
+ check_mailbox = dict(mailbox=event_mailbox, new=event_new,
+ old=event_old)
+
+ if check_mailbox not in MAILBOXES:
+ LOGGER.error("Unexpected values in MWIGet event")
+ self.fail_test()
+
+ query = dict(id=event_mailbox)
+ rt_row = self.rt_data.retrieve_row('mwi', query)
+ if not rt_row:
+ LOGGER.error("Mailbox %s in AMI event is not in realtime" %
+ event_mailbox)
+ self.fail_test()
+ LOGGER.debug("Row retrieved from realtime: %s" % rt_row)
+
+ rt_mailbox = rt_row.get('id', '')
+ rt_new = rt_row.get('msgs_new', '')
+ rt_old = rt_row.get('msgs_old', '')
+
+ if event_mailbox != rt_mailbox or \
+ rt_new != event_new or \
+ rt_old != event_old:
+ LOGGER.error("AMI event and realtime data do not match")
+ self.fail_test()
+
+ def mwi_get_complete_callback(self, ami, event):
+ if self.retrieved_mailboxes != EXPECTED_MAILBOXES:
+ LOGGER.error("Did not retrieve the expected number of mailboxes,"
+ " %d != %d" % (self.retrieved_mailboxes,
+ EXPECTED_MAILBOXES))
+ self.fail_test()
+ else:
+ self.pass_test()
+
+
+def check_it(rt_data, test_object, ami):
+ test = MultiTest(rt_data, test_object, ami)
+
+ ami.registerEvent('MWIGet', test.mwi_get_callback)
+ ami.registerEvent('MWIGetComplete', test.mwi_get_complete_callback)
+ message = {
+ 'Action': 'MWIGet',
+ 'Mailbox': '/^/',
+ }
+ ami.sendMessage(message)
Propchange: asterisk/trunk/tests/realtime/multi/multi.py
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: asterisk/trunk/tests/realtime/multi/multi.py
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: asterisk/trunk/tests/realtime/multi/multi.py
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: asterisk/trunk/tests/realtime/multi/test-config.yaml
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/realtime/multi/test-config.yaml?view=auto&rev=5145
==============================================================================
--- asterisk/trunk/tests/realtime/multi/test-config.yaml (added)
+++ asterisk/trunk/tests/realtime/multi/test-config.yaml Thu Jun 19 16:34:48 2014
@@ -1,0 +1,47 @@
+testinfo:
+ summary: 'Realtime multi test'
+ description: |
+ 'This tests the ability of Asterisk to retrieve multiple entries from realtime.
+ Two MWI mailboxes are preloaded in realtime, and Asterisk is tasked with
+ retrieving them when asked.'
+
+
+properties:
+ minversion: '12.0.0'
+ dependencies:
+ - python: 'twisted'
+ - python: 'starpy'
+ - asterisk: 'res_mwi_external'
+ - asterisk: 'res_mwi_external_ami'
+ - asterisk: 'res_config_curl'
+ - asterisk: 'func_curl'
+ tags:
+ - realtime
+ - mwi_external
+
+test-modules:
+ add-test-to-search-path: 'True'
+ test-object:
+ config-section: test-case-config
+ typename: 'test_case.TestCaseModule'
+ modules:
+ -
+ config-section: 'realtime-config'
+ typename: 'realtime_test_module.RealtimeTestModule'
+
+test-case-config:
+ connect-ami: 'True'
+
+realtime-config:
+ entry_module: 'multi'
+ entry_method: 'check_it'
+ data:
+ mwi:
+ -
+ id: 'Dazed'
+ msgs_new: '5'
+ msgs_old: '4'
+ -
+ id: 'Confused'
+ msgs_new: '6'
+ msgs_old: '8'
Propchange: asterisk/trunk/tests/realtime/multi/test-config.yaml
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: asterisk/trunk/tests/realtime/multi/test-config.yaml
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: asterisk/trunk/tests/realtime/multi/test-config.yaml
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: asterisk/trunk/tests/realtime/single/configs/ast1/extconfig.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/realtime/single/configs/ast1/extconfig.conf?view=auto&rev=5145
==============================================================================
--- asterisk/trunk/tests/realtime/single/configs/ast1/extconfig.conf (added)
+++ asterisk/trunk/tests/realtime/single/configs/ast1/extconfig.conf Thu Jun 19 16:34:48 2014
@@ -1,0 +1,2 @@
+[settings]
+mailboxes => curl,http://localhost:8000/mwi
Propchange: asterisk/trunk/tests/realtime/single/configs/ast1/extconfig.conf
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: asterisk/trunk/tests/realtime/single/configs/ast1/extconfig.conf
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: asterisk/trunk/tests/realtime/single/configs/ast1/extconfig.conf
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: asterisk/trunk/tests/realtime/single/configs/ast1/sorcery.conf
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/realtime/single/configs/ast1/sorcery.conf?view=auto&rev=5145
==============================================================================
--- asterisk/trunk/tests/realtime/single/configs/ast1/sorcery.conf (added)
+++ asterisk/trunk/tests/realtime/single/configs/ast1/sorcery.conf Thu Jun 19 16:34:48 2014
@@ -1,0 +1,2 @@
+[res_mwi_external]
+mailboxes=realtime,mailboxes
Propchange: asterisk/trunk/tests/realtime/single/configs/ast1/sorcery.conf
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: asterisk/trunk/tests/realtime/single/configs/ast1/sorcery.conf
[... 788 lines stripped ...]
More information about the asterisk-commits
mailing list