<p>Jenkins2 <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/9315">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Kevin Harwell: Looks good to me, but someone else must approve
  George Joseph: Looks good to me, approved
  Jenkins2: Approved for Submit

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Begin work on python3 compatability.<br><br>* Use explicit relative import syntax.<br>* Correct syntax for print and except.<br>* Fix incorrect usage of IOError in astcdr.py - IOError is not a tuple.<br>* Use items, values instead of iteritems, itervalues.<br>* Use OrderedDict from collections if available (python 2.7+).<br>* Do not unpack tuples from argument list, save tuple to variable then<br>  unpack within code.<br>* Use exception handler to import Python3 version of urlencode if the<br>  Python2 version fails to import.<br>* Remove unused import unittest from opensslversion.<br>* Move executable tests to separate files to resolve issues with<br>  relative imports:<br>  - buildoptions<br>  - cdr<br>  - cel<br>  - channel_test_condition<br>  - config<br>  - lock_test_condition<br>  - sip_dialog_test_condition (disabled)<br>  - sippversion<br>* Modify utils_socket test to use new harness_shared module.<br>* Remove unused executable flag and shebang from phones.py.<br>* Update self_test script to run tests from new location.<br>* Remove ability to use config.py to display a config file.<br>* Allow PYTHON environmental variable to tell run-local to use a<br>  specific python binary.<br>* Execute test_runner.py using python -m syntax.  This avoids conflicts<br>  between needing to use relative imports for modules but not being<br>  allowed to use them in executable scripts.<br>* Encode self.realbase for use with md5 function.<br>* Remove executable flag and shebang from test_runner.py.  Remove<br>  redundant appending of sys.path.<br>* Modify output for running test and status to print the name of the<br>  test instead of the command used to run the test.<br>* Use key= sorter argument to compare sizes of tmp filesystems.  This<br>  may cause us to use /var/tmp instead of /tmp even if /var/tmp has only<br>  a few kilobytes more space but python3 does not support cmp= argument<br>  to sorter.<br>* Decode binary to utf-8<br>  - CLI output<br>  - runtest.py subprocess output<br>  - XML printed to stdout<br>  - SyncAMI data<br>  - SIPp runtime and version output<br>* Update test_runner.load_and_parse_module to check for the module in<br>  lib/python/asterisk, make required adjustments if found.<br>* Update __strip_illegal_xml_chars to work with unicode version of<br>  str.translate which does not support second argument.<br>* Update import statements from all *.py modules within tests/ to use<br>  absolute names for asterisk modules.  This is required for python2 to<br>  continue functioning with the new way that test_runner must be executed.<br>  No attempt has been made to fix python3 compatibility issues in these<br>  modules.<br><br>Some tests may pass using python3.  Doing so requires using an updated<br>starpy and no python sources within tests/ have been updated.  Unit<br>tests from lib/python can be run against python2 and python3 using<br>./self_test.<br><br>The goal of this is for all tests to continue functioning using python2.<br>Some tests may work under python3 but individual tests will need to be<br>addressed separately.  For tests which do not contain a run-test script<br>the version of python used to execute ./runtests.py will be used:<br>./runtests.py -t tests/manager/originate<br>python2 ./runtests.py -t tests/manager/originate<br>python3 ./runtests.py -t tests/manager/originate<br><br>These commands will be python, python2 and python3 respectively.  The<br>first example where no interpreter is specified uses the shebang from<br>./runtests.py.  The interpreter used by ./run-local can be controlled<br>by setting PYTHON environmental variable:<br>PYTHON=python3 ./run-local run -t tests/manager/originate<br><br>Change-Id: If76c2d3e11e4ab4552d0df7841287c8bb2de7918<br>---<br>M lib/python/asterisk/ami.py<br>M lib/python/asterisk/apptest.py<br>M lib/python/asterisk/ari.py<br>M lib/python/asterisk/astconfigparser.py<br>M lib/python/asterisk/astcsv.py<br>M lib/python/asterisk/astdicts.py<br>M lib/python/asterisk/asterisk.py<br>M lib/python/asterisk/bridge_test_case.py<br>M lib/python/asterisk/buildoptions.py<br>M lib/python/asterisk/cdr.py<br>M lib/python/asterisk/cel.py<br>M lib/python/asterisk/channel_test_condition.py<br>M lib/python/asterisk/confbridge.py<br>M lib/python/asterisk/config.py<br>M lib/python/asterisk/fd_test_condition.py<br>M lib/python/asterisk/lock_test_condition.py<br>M lib/python/asterisk/matcher.py<br>M lib/python/asterisk/matcher_listener.py<br>M lib/python/asterisk/opensslversion.py<br>M lib/python/asterisk/originate.py<br>M lib/python/asterisk/pcap.py<br>M lib/python/asterisk/phones.py<br>M lib/python/asterisk/pjsua_mod.py<br>M lib/python/asterisk/pluggable_modules.py<br>M lib/python/asterisk/pluggable_registry.py<br>M lib/python/asterisk/realtime_converter.py<br>M lib/python/asterisk/realtime_odbc_module.py<br>M lib/python/asterisk/realtime_test_module.py<br>A lib/python/asterisk/self_test/harness_shared.py<br>A lib/python/asterisk/self_test/locks-backtrace.txt<br>A lib/python/asterisk/self_test/locks-fail.txt<br>A lib/python/asterisk/self_test/locks-large-multiple-object.txt<br>A lib/python/asterisk/self_test/locks-multiple-objects-no-backtrace.txt<br>A lib/python/asterisk/self_test/locks-pass.txt<br>A lib/python/asterisk/self_test/locks-single-object-no-held-info.txt<br>A lib/python/asterisk/self_test/locks-single-object.txt<br>A lib/python/asterisk/self_test/test_buildoptions.py<br>A lib/python/asterisk/self_test/test_cdr.py<br>A lib/python/asterisk/self_test/test_cel.py<br>A lib/python/asterisk/self_test/test_channel_test_condition.py<br>A lib/python/asterisk/self_test/test_config.py<br>A lib/python/asterisk/self_test/test_lock_test_condition.py<br>R lib/python/asterisk/self_test/test_matcher.py<br>A lib/python/asterisk/self_test/test_sip_dialog_test_condition.py.txt<br>A lib/python/asterisk/self_test/test_sippversion.py<br>M lib/python/asterisk/self_test/test_utils_socket.py<br>M lib/python/asterisk/sip_channel_test_condition.py<br>M lib/python/asterisk/sip_dialog_test_condition.py<br>M lib/python/asterisk/sipp.py<br>M lib/python/asterisk/sippversion.py<br>M lib/python/asterisk/syncami.py<br>M lib/python/asterisk/test_case.py<br>M lib/python/asterisk/test_conditions.py<br>M lib/python/asterisk/test_config.py<br>M lib/python/asterisk/test_runner.py<br>M lib/python/asterisk/test_suite_utils.py<br>M lib/python/asterisk/thread_test_condition.py<br>M lib/python/asterisk/voicemail.py<br>M lib/python/rlmi.py<br>M lib/python/sip_message.py<br>M run-local<br>M runtests.py<br>M self_test<br>M tests/cdr/cdr-tests.py<br>M tests/cdr/sqlite3/cdr_sqlite3.py<br>M tests/channels/SIP/tcpauthlimit/sipp_scenario.py<br>M tests/channels/pjsip/registration/inbound/nominal/contact_acl/ipv4/scenario_generator.py<br>M tests/channels/pjsip/registration/inbound/nominal/contact_acl/ipv6/scenario_generator.py<br>M tests/channels/pjsip/subscriptions/presence/verify_bodies/presence.py<br>M tests/channels/pjsip/subscriptions/rls/rls_test.py<br>M tests/channels/pjsip/transfers/attended_transfer/nominal/packet_sniffer.py<br>M tests/codecs/audio_analyzer.py<br>M tests/codecs/rtp_analyzer.py<br>M tests/hep/hep_capture_node.py<br>M tests/manager/config/ManagerConfigTest.py<br>M tests/manager/device_state_changed/ami_device_state.py<br>M tests/manager/device_state_list/ami_device_state_list.py<br>M tests/manager/exten_state_list/ami_exten_state_list.py<br>M tests/manager/presence_state_changed/ami_presence_state.py<br>M tests/manager/presence_state_list/ami_presence_state_list.py<br>M tests/pbx/manager_extensions/ami_extension_control.py<br>M tests/rest_api/external_interaction/attended_transfer/attended_transfer.py<br>M tests/rest_api/external_interaction/blind_transfer/call_transfer.py<br>M tests/rest_api/message/message_modules.py<br>M usage.py<br>85 files changed, 1,910 insertions(+), 1,841 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/lib/python/asterisk/ami.py b/lib/python/asterisk/ami.py</span><br><span>index fb52d1c..2e0fe4e 100644</span><br><span>--- a/lib/python/asterisk/ami.py</span><br><span>+++ b/lib/python/asterisk/ami.py</span><br><span>@@ -13,7 +13,8 @@</span><br><span> import logging</span><br><span> import re</span><br><span> import json</span><br><span style="color: hsl(0, 100%, 40%);">-from pluggable_registry import PLUGGABLE_EVENT_REGISTRY,\</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_runner import load_and_parse_module</span><br><span style="color: hsl(120, 100%, 40%);">+from .pluggable_registry import PLUGGABLE_EVENT_REGISTRY,\</span><br><span>     PLUGGABLE_ACTION_REGISTRY, var_replace</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span>@@ -361,7 +362,7 @@</span><br><span>             lower_key = key.lower()</span><br><span>             if lower_key == 'extra':</span><br><span>                 value = dict((key.lower(), value)</span><br><span style="color: hsl(0, 100%, 40%);">-                             for key, value in value.iteritems())</span><br><span style="color: hsl(120, 100%, 40%);">+                             for key, value in value.items())</span><br><span>             self.requirements[lower_key] = value</span><br><span>         self.orderings = requirements.get('partialorder') or []</span><br><span>         self.named_id = requirements.get('id')</span><br><span>@@ -529,8 +530,7 @@</span><br><span> </span><br><span>     def event_callback(self, ami, event):</span><br><span>         """Callback called when an event is received from AMI"""</span><br><span style="color: hsl(0, 100%, 40%);">-        callback_module = __import__(self.callback_module)</span><br><span style="color: hsl(0, 100%, 40%);">-        method = getattr(callback_module, self.callback_method)</span><br><span style="color: hsl(120, 100%, 40%);">+        method = load_and_parse_module(self.callback_module + '.' + self.callback_method)</span><br><span>         self.passed = method(ami, event)</span><br><span>         if self.passed is None:</span><br><span>             LOGGER.error("Callback %s.%s returned None instead of a boolean",</span><br><span>@@ -828,7 +828,7 @@</span><br><span> </span><br><span> def replace_ami_vars(mydict, values):</span><br><span>     outdict = {}</span><br><span style="color: hsl(0, 100%, 40%);">-    for key, value in mydict.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+    for key, value in mydict.items():</span><br><span>         outdict[key] = var_replace(value, values)</span><br><span> </span><br><span>     return outdict</span><br><span>diff --git a/lib/python/asterisk/apptest.py b/lib/python/asterisk/apptest.py</span><br><span>index 1fc8496..2c33ad9 100644</span><br><span>--- a/lib/python/asterisk/apptest.py</span><br><span>+++ b/lib/python/asterisk/apptest.py</span><br><span>@@ -18,8 +18,8 @@</span><br><span> from twisted.internet import reactor, defer</span><br><span> </span><br><span> sys.path.append("lib/python")</span><br><span style="color: hsl(0, 100%, 40%);">-from test_case import TestCase</span><br><span style="color: hsl(0, 100%, 40%);">-from ami import AMIEventInstance</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_case import TestCase</span><br><span style="color: hsl(120, 100%, 40%);">+from .ami import AMIEventInstance</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>@@ -165,7 +165,7 @@</span><br><span>         A Scenario is considered done if all results have been met and</span><br><span>         all expected actions have been executed.</span><br><span>         """</span><br><span style="color: hsl(0, 100%, 40%);">-        return (all(self._expected_results.itervalues()) and</span><br><span style="color: hsl(120, 100%, 40%);">+        return (all(self._expected_results.values()) and</span><br><span>                 all(i.ran_actions or i.unexpected</span><br><span>                     for i in self._event_instances))</span><br><span> </span><br><span>diff --git a/lib/python/asterisk/ari.py b/lib/python/asterisk/ari.py</span><br><span>index da19cee..23672dd 100644</span><br><span>--- a/lib/python/asterisk/ari.py</span><br><span>+++ b/lib/python/asterisk/ari.py</span><br><span>@@ -12,12 +12,16 @@</span><br><span> import re</span><br><span> import requests</span><br><span> import traceback</span><br><span style="color: hsl(0, 100%, 40%);">-import urllib</span><br><span style="color: hsl(120, 100%, 40%);">+try:</span><br><span style="color: hsl(120, 100%, 40%);">+    from urllib.parse import urlencode</span><br><span style="color: hsl(120, 100%, 40%);">+except:</span><br><span style="color: hsl(120, 100%, 40%);">+    from urllib import urlencode</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from test_case import TestCase</span><br><span style="color: hsl(0, 100%, 40%);">-from pluggable_registry import PLUGGABLE_EVENT_REGISTRY,\</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_case import TestCase</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_runner import load_and_parse_module</span><br><span style="color: hsl(120, 100%, 40%);">+from .pluggable_registry import PLUGGABLE_EVENT_REGISTRY,\</span><br><span>     PLUGGABLE_ACTION_REGISTRY, var_replace</span><br><span style="color: hsl(0, 100%, 40%);">-from test_suite_utils import all_match</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_suite_utils import all_match</span><br><span> from twisted.internet import reactor</span><br><span> try:</span><br><span>     from autobahn.websocket import WebSocketClientFactory, \</span><br><span>@@ -332,7 +336,7 @@</span><br><span>         """</span><br><span>         url = "ws://%s:%d/ari/events?%s" % \</span><br><span>               (host, port,</span><br><span style="color: hsl(0, 100%, 40%);">-               urllib.urlencode({'app': apps, 'api_key': '%s:%s' % userpass}))</span><br><span style="color: hsl(120, 100%, 40%);">+               urlencode({'app': apps, 'api_key': '%s:%s' % userpass}))</span><br><span>         if subscribe_all:</span><br><span>             url += '&subscribeAll=true'</span><br><span>         LOGGER.info("WebSocketClientFactory(url=%s)", url)</span><br><span>@@ -560,7 +564,7 @@</span><br><span>         url = self.ari.build_url(uri)</span><br><span>         requests_method = getattr(requests, self.method)</span><br><span>         params = dict((key, var_replace(val, values))</span><br><span style="color: hsl(0, 100%, 40%);">-                      for key, val in self.params.iteritems())</span><br><span style="color: hsl(120, 100%, 40%);">+                      for key, val in self.params.items())</span><br><span> </span><br><span>         response = requests_method(</span><br><span>             url,</span><br><span>@@ -580,7 +584,7 @@</span><br><span>                              response.status_code, response.text)</span><br><span>                 return False</span><br><span>         else:</span><br><span style="color: hsl(0, 100%, 40%);">-            if response.status_code / 100 != 2:</span><br><span style="color: hsl(120, 100%, 40%);">+            if response.status_code // 100 != 2:</span><br><span>                 LOGGER.error('sent %s %s %s response %d %s',</span><br><span>                              self.method, self.uri, self.params,</span><br><span>                              response.status_code, response.text)</span><br><span>@@ -606,8 +610,7 @@</span><br><span> </span><br><span>         callback = self.instance_config.get('callback')</span><br><span>         if callback:</span><br><span style="color: hsl(0, 100%, 40%);">-            module = __import__(callback['module'])</span><br><span style="color: hsl(0, 100%, 40%);">-            self.callback = getattr(module, callback['method'])</span><br><span style="color: hsl(120, 100%, 40%);">+            self.callback = load_and_parse_module(callback['module'] + '.' + callback['method'])</span><br><span>         else:</span><br><span>             # No callback; just use a no-op</span><br><span>             self.callback = lambda *args, **kwargs: True</span><br><span>diff --git a/lib/python/asterisk/astconfigparser.py b/lib/python/asterisk/astconfigparser.py</span><br><span>index dc79e80..e8da255 100644</span><br><span>--- a/lib/python/asterisk/astconfigparser.py</span><br><span>+++ b/lib/python/asterisk/astconfigparser.py</span><br><span>@@ -8,8 +8,7 @@</span><br><span> import re</span><br><span> import itertools</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from astdicts import OrderedDict</span><br><span style="color: hsl(0, 100%, 40%);">-from astdicts import MultiOrderedDict</span><br><span style="color: hsl(120, 100%, 40%);">+from .astdicts import OrderedDict, MultiOrderedDict</span><br><span> </span><br><span> </span><br><span> def merge_values(left, right, key):</span><br><span>@@ -270,11 +269,11 @@</span><br><span> </span><br><span> def write_dicts(config_file, mdicts):</span><br><span>     """Write the contents of the mdicts to the specified config file"""</span><br><span style="color: hsl(0, 100%, 40%);">-    for section, sect_list in mdicts.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+    for section, sect_list in mdicts.items():</span><br><span>         # every section contains a list of dictionaries</span><br><span>         for sect in sect_list:</span><br><span>             config_file.write("[%s]\n" % section)</span><br><span style="color: hsl(0, 100%, 40%);">-            for key, val_list in sect.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+            for key, val_list in sect.items():</span><br><span>                 # every value is also a list</span><br><span>                 for v in val_list:</span><br><span>                     key_val = key</span><br><span>@@ -355,7 +354,7 @@</span><br><span>         if self._includes:</span><br><span>             res.extend(list(itertools.chain(*[</span><br><span>                 incl.get_sections(key, attr, searched)</span><br><span style="color: hsl(0, 100%, 40%);">-                for incl in self._includes.itervalues()])))</span><br><span style="color: hsl(120, 100%, 40%);">+                for incl in self._includes.values()])))</span><br><span>         if self._parent:</span><br><span>             res += self._parent.get_sections(key, attr, searched)</span><br><span>         return res</span><br><span>@@ -445,7 +444,7 @@</span><br><span>             with open(filename, 'rt') as config_file:</span><br><span>                 self._read(config_file, sect)</span><br><span>         except IOError:</span><br><span style="color: hsl(0, 100%, 40%);">-            print "Could not open file ", filename, " for reading"</span><br><span style="color: hsl(120, 100%, 40%);">+            print("Could not open file ", filename, " for reading")</span><br><span> </span><br><span>     def _read(self, config_file, sect):</span><br><span>         """Parse configuration information from the config_file"""</span><br><span>@@ -478,7 +477,7 @@</span><br><span>     def write(self, config_file):</span><br><span>         """Write configuration information out to a file"""</span><br><span>         try:</span><br><span style="color: hsl(0, 100%, 40%);">-            for key, val in self._includes.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+            for key, val in self._includes.items():</span><br><span>                 val.write(key)</span><br><span>                 config_file.write('#include "%s"\n' % key)</span><br><span> </span><br><span>@@ -490,4 +489,4 @@</span><br><span>                 with open(config_file, 'wt') as fp:</span><br><span>                     self.write(fp)</span><br><span>             except IOError:</span><br><span style="color: hsl(0, 100%, 40%);">-                print "Could not open file ", config_file, " for writing"</span><br><span style="color: hsl(120, 100%, 40%);">+                print("Could not open file ", config_file, " for writing")</span><br><span>diff --git a/lib/python/asterisk/astcsv.py b/lib/python/asterisk/astcsv.py</span><br><span>index 67d25d3..7619bf5 100644</span><br><span>--- a/lib/python/asterisk/astcsv.py</span><br><span>+++ b/lib/python/asterisk/astcsv.py</span><br><span>@@ -1,4 +1,3 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python</span><br><span> """Asterisk CSV-based testing</span><br><span> </span><br><span> This module implements the basic CSV testing backend for things like</span><br><span>@@ -11,7 +10,6 @@</span><br><span> the GNU General Public License Version 2.</span><br><span> """</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-import unittest</span><br><span> import sys</span><br><span> import csv</span><br><span> import re</span><br><span>@@ -57,10 +55,10 @@</span><br><span>         else:</span><br><span>             cmp_fn = (lambda x, y: str(x).lower() == str(y).lower())</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        for key, value in self.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+        for key, value in self.items():</span><br><span>             if None not in (value, other.get(key)) and not cmp_fn(value, other.get(key)):</span><br><span>                 if not silent:</span><br><span style="color: hsl(0, 100%, 40%);">-                    LOGGER.warn("CSV MATCH FAILED, Expected: %s: '%s' "</span><br><span style="color: hsl(120, 100%, 40%);">+                    LOGGER.warning("CSV MATCH FAILED, Expected: %s: '%s' "</span><br><span>                                 "Got: %s: '%s'" % (key, value, key,</span><br><span>                                                    other.get(key)))</span><br><span>                 return False</span><br><span>@@ -70,9 +68,9 @@</span><br><span>         """Retrieve a value from the specified column key"""</span><br><span>         return self.__columns.get(key)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def iteritems(self):</span><br><span style="color: hsl(120, 100%, 40%);">+    def items(self):</span><br><span>         """Iterate over the values in the columns"""</span><br><span style="color: hsl(0, 100%, 40%);">-        return self.__columns.iteritems()</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.__columns.items()</span><br><span> </span><br><span>     def __str__(self):</span><br><span>         return ",".join(["\"%s\"" % (self.__dict__[x]) for x in self.__fields])</span><br><span>@@ -94,12 +92,14 @@</span><br><span>         self.__records = []</span><br><span> </span><br><span>         csvreader = None</span><br><span style="color: hsl(120, 100%, 40%);">+        csvfile = None</span><br><span> </span><br><span>         try:</span><br><span style="color: hsl(0, 100%, 40%);">-            csvreader = csv.DictReader(open(self.filename, "r"), fields, ",")</span><br><span style="color: hsl(0, 100%, 40%);">-        except IOError as (errno, strerror):</span><br><span style="color: hsl(120, 100%, 40%);">+            csvfile = open(self.filename, "r")</span><br><span style="color: hsl(120, 100%, 40%);">+            csvreader = csv.DictReader(csvfile, fields, ",")</span><br><span style="color: hsl(120, 100%, 40%);">+        except IOError as e:</span><br><span>             LOGGER.error("IOError %d[%s] while opening file '%s'" %</span><br><span style="color: hsl(0, 100%, 40%);">-                         (errno, strerror, self.filename))</span><br><span style="color: hsl(120, 100%, 40%);">+                         (e.errno, e.strerror, self.filename))</span><br><span>         except:</span><br><span>             LOGGER.error("Unexpected error: %s" % (sys.exc_info()[0]))</span><br><span> </span><br><span>@@ -111,6 +111,8 @@</span><br><span>             record = self.row_factory(**row)</span><br><span>             self.__records.append(record)</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+        csvfile.close()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>     def __len__(self):</span><br><span>         return len(self.__records)</span><br><span> </span><br><span>@@ -125,7 +127,7 @@</span><br><span>         each record"""</span><br><span> </span><br><span>         if not partial and (len(self) != len(other)):</span><br><span style="color: hsl(0, 100%, 40%);">-            LOGGER.warn("CSV MATCH FAILED, different number of records, "</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGGER.warning("CSV MATCH FAILED, different number of records, "</span><br><span>                         "self=%d and other=%d" % (len(self), len(other)))</span><br><span>             return False</span><br><span> </span><br><span>@@ -147,7 +149,7 @@</span><br><span>             size = len(list_a)</span><br><span> </span><br><span>             # attempt two orderings: forward and reversed</span><br><span style="color: hsl(0, 100%, 40%);">-            guess_orders = (range(size), list(reversed(range(size))))</span><br><span style="color: hsl(120, 100%, 40%);">+            guess_orders = (list(range(size)), list(reversed(range(size))))</span><br><span>             found_orders = []</span><br><span> </span><br><span>             for guess_order in guess_orders:</span><br><span>@@ -185,7 +187,7 @@</span><br><span>             # have it complain immediately.</span><br><span>             for i, item in enumerate(self):</span><br><span>                 if not item.match(other[i], exact=exactness):</span><br><span style="color: hsl(0, 100%, 40%);">-                    LOGGER.warn("Failed to match entry %d" % (i,))</span><br><span style="color: hsl(120, 100%, 40%);">+                    LOGGER.warning("Failed to match entry %d" % (i,))</span><br><span>                     return False</span><br><span>             assert False</span><br><span> </span><br><span>@@ -193,7 +195,7 @@</span><br><span>             pass  # joy!</span><br><span> </span><br><span>         elif len(matches) > 1:</span><br><span style="color: hsl(0, 100%, 40%);">-            LOGGER.warn("More than one CSV permutation results in success")</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGGER.warning("More than one CSV permutation results in success")</span><br><span> </span><br><span>         return True</span><br><span> </span><br><span>@@ -205,9 +207,6 @@</span><br><span>         try:</span><br><span>             open(self.filename, "w").close()</span><br><span>         except:</span><br><span style="color: hsl(0, 100%, 40%);">-            LOGGER.warn("Unable to empty CSV file %s" % (self.filename))</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-if __name__ == '__main__':</span><br><span style="color: hsl(0, 100%, 40%);">-    unittest.main()</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGGER.warning("Unable to empty CSV file %s" % (self.filename))</span><br><span> </span><br><span> # vim:sw=4:ts=4:expandtab:textwidth=79</span><br><span>diff --git a/lib/python/asterisk/astdicts.py b/lib/python/asterisk/astdicts.py</span><br><span>index ae63075..4e70eec 100644</span><br><span>--- a/lib/python/asterisk/astdicts.py</span><br><span>+++ b/lib/python/asterisk/astdicts.py</span><br><span>@@ -3,260 +3,264 @@</span><br><span> # copied from http://code.activestate.com/recipes/576693/</span><br><span> </span><br><span> try:</span><br><span style="color: hsl(0, 100%, 40%);">-    from thread import get_ident as _get_ident</span><br><span style="color: hsl(120, 100%, 40%);">+    # Use builtin OrderedDict() from Python2.7.</span><br><span style="color: hsl(120, 100%, 40%);">+    from collections import OrderedDict</span><br><span> except ImportError:</span><br><span style="color: hsl(0, 100%, 40%);">-    from dummy_thread import get_ident as _get_ident</span><br><span style="color: hsl(120, 100%, 40%);">+    try:</span><br><span style="color: hsl(120, 100%, 40%);">+        from thread import get_ident as _get_ident</span><br><span style="color: hsl(120, 100%, 40%);">+    except ImportError:</span><br><span style="color: hsl(120, 100%, 40%);">+        from dummy_thread import get_ident as _get_ident</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-try:</span><br><span style="color: hsl(0, 100%, 40%);">-    from _abcoll import KeysView, ValuesView, ItemsView</span><br><span style="color: hsl(0, 100%, 40%);">-except ImportError:</span><br><span style="color: hsl(0, 100%, 40%);">-    pass</span><br><span style="color: hsl(120, 100%, 40%);">+    try:</span><br><span style="color: hsl(120, 100%, 40%);">+        from _abcoll import KeysView, ValuesView, ItemsView</span><br><span style="color: hsl(120, 100%, 40%);">+    except ImportError:</span><br><span style="color: hsl(120, 100%, 40%);">+        pass</span><br><span> </span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-class OrderedDict(dict):</span><br><span style="color: hsl(0, 100%, 40%);">-    'Dictionary that remembers insertion order'</span><br><span style="color: hsl(0, 100%, 40%);">-    # An inherited dict maps keys to values.</span><br><span style="color: hsl(0, 100%, 40%);">-    # The inherited dict provides __getitem__, __len__, __contains__, and get.</span><br><span style="color: hsl(0, 100%, 40%);">-    # The remaining methods are order-aware.</span><br><span style="color: hsl(0, 100%, 40%);">-    # Big-O running times for all methods are the same as for regular dictionaries.</span><br><span style="color: hsl(120, 100%, 40%);">+    class OrderedDict(dict):</span><br><span style="color: hsl(120, 100%, 40%);">+        'Dictionary that remembers insertion order'</span><br><span style="color: hsl(120, 100%, 40%);">+        # An inherited dict maps keys to values.</span><br><span style="color: hsl(120, 100%, 40%);">+        # The inherited dict provides __getitem__, __len__, __contains__, and get.</span><br><span style="color: hsl(120, 100%, 40%);">+        # The remaining methods are order-aware.</span><br><span style="color: hsl(120, 100%, 40%);">+        # Big-O running times for all methods are the same as for regular dictionaries.</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    # The internal self.__map dictionary maps keys to links in a doubly linked list.</span><br><span style="color: hsl(0, 100%, 40%);">-    # The circular doubly linked list starts and ends with a sentinel element.</span><br><span style="color: hsl(0, 100%, 40%);">-    # The sentinel element never gets deleted (this simplifies the algorithm).</span><br><span style="color: hsl(0, 100%, 40%);">-    # Each link is stored as a list of length three:  [PREV, NEXT, KEY].</span><br><span style="color: hsl(120, 100%, 40%);">+        # The internal self.__map dictionary maps keys to links in a doubly linked list.</span><br><span style="color: hsl(120, 100%, 40%);">+        # The circular doubly linked list starts and ends with a sentinel element.</span><br><span style="color: hsl(120, 100%, 40%);">+        # The sentinel element never gets deleted (this simplifies the algorithm).</span><br><span style="color: hsl(120, 100%, 40%);">+        # Each link is stored as a list of length three:  [PREV, NEXT, KEY].</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self, *args, **kwds):</span><br><span style="color: hsl(0, 100%, 40%);">-        '''Initialize an ordered dictionary.  Signature is the same as for</span><br><span style="color: hsl(0, 100%, 40%);">-        regular dictionaries, but keyword arguments are not recommended</span><br><span style="color: hsl(0, 100%, 40%);">-        because their insertion order is arbitrary.</span><br><span style="color: hsl(120, 100%, 40%);">+        def __init__(self, *args, **kwds):</span><br><span style="color: hsl(120, 100%, 40%);">+            '''Initialize an ordered dictionary.  Signature is the same as for</span><br><span style="color: hsl(120, 100%, 40%);">+            regular dictionaries, but keyword arguments are not recommended</span><br><span style="color: hsl(120, 100%, 40%);">+            because their insertion order is arbitrary.</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        '''</span><br><span style="color: hsl(0, 100%, 40%);">-        if len(args) > 1:</span><br><span style="color: hsl(0, 100%, 40%);">-            raise TypeError('expected at most 1 arguments, got %d' % len(args))</span><br><span style="color: hsl(0, 100%, 40%);">-        try:</span><br><span style="color: hsl(0, 100%, 40%);">-            self.__root</span><br><span style="color: hsl(0, 100%, 40%);">-        except AttributeError:</span><br><span style="color: hsl(0, 100%, 40%);">-            self.__root = root = []                     # sentinel node</span><br><span style="color: hsl(0, 100%, 40%);">-            root[:] = [root, root, None]</span><br><span style="color: hsl(0, 100%, 40%);">-            self.__map = {}</span><br><span style="color: hsl(0, 100%, 40%);">-        self.__update(*args, **kwds)</span><br><span style="color: hsl(120, 100%, 40%);">+            '''</span><br><span style="color: hsl(120, 100%, 40%);">+            if len(args) > 1:</span><br><span style="color: hsl(120, 100%, 40%);">+                raise TypeError('expected at most 1 arguments, got %d' % len(args))</span><br><span style="color: hsl(120, 100%, 40%);">+            try:</span><br><span style="color: hsl(120, 100%, 40%);">+                self.__root</span><br><span style="color: hsl(120, 100%, 40%);">+            except AttributeError:</span><br><span style="color: hsl(120, 100%, 40%);">+                self.__root = root = []                     # sentinel node</span><br><span style="color: hsl(120, 100%, 40%);">+                root[:] = [root, root, None]</span><br><span style="color: hsl(120, 100%, 40%);">+                self.__map = {}</span><br><span style="color: hsl(120, 100%, 40%);">+            self.__update(*args, **kwds)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def __setitem__(self, key, value, dict_setitem=dict.__setitem__):</span><br><span style="color: hsl(0, 100%, 40%);">-        'od.__setitem__(i, y) <==> od[i]=y'</span><br><span style="color: hsl(0, 100%, 40%);">-        # Setting a new item creates a new link which goes at the end of the linked</span><br><span style="color: hsl(0, 100%, 40%);">-        # list, and the inherited dictionary is updated with the new key/value pair.</span><br><span style="color: hsl(0, 100%, 40%);">-        if key not in self:</span><br><span style="color: hsl(120, 100%, 40%);">+        def __setitem__(self, key, value, dict_setitem=dict.__setitem__):</span><br><span style="color: hsl(120, 100%, 40%);">+            'od.__setitem__(i, y) <==> od[i]=y'</span><br><span style="color: hsl(120, 100%, 40%);">+            # Setting a new item creates a new link which goes at the end of the linked</span><br><span style="color: hsl(120, 100%, 40%);">+            # list, and the inherited dictionary is updated with the new key/value pair.</span><br><span style="color: hsl(120, 100%, 40%);">+            if key not in self:</span><br><span style="color: hsl(120, 100%, 40%);">+                root = self.__root</span><br><span style="color: hsl(120, 100%, 40%);">+                last = root[0]</span><br><span style="color: hsl(120, 100%, 40%);">+                last[1] = root[0] = self.__map[key] = [last, root, key]</span><br><span style="color: hsl(120, 100%, 40%);">+            dict_setitem(self, key, value)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def __delitem__(self, key, dict_delitem=dict.__delitem__):</span><br><span style="color: hsl(120, 100%, 40%);">+            'od.__delitem__(y) <==> del od[y]'</span><br><span style="color: hsl(120, 100%, 40%);">+            # Deleting an existing item uses self.__map to find the link which is</span><br><span style="color: hsl(120, 100%, 40%);">+            # then removed by updating the links in the predecessor and successor nodes.</span><br><span style="color: hsl(120, 100%, 40%);">+            dict_delitem(self, key)</span><br><span style="color: hsl(120, 100%, 40%);">+            link_prev, link_next, key = self.__map.pop(key)</span><br><span style="color: hsl(120, 100%, 40%);">+            link_prev[1] = link_next</span><br><span style="color: hsl(120, 100%, 40%);">+            link_next[0] = link_prev</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def __iter__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+            'od.__iter__() <==> iter(od)'</span><br><span>             root = self.__root</span><br><span style="color: hsl(0, 100%, 40%);">-            last = root[0]</span><br><span style="color: hsl(0, 100%, 40%);">-            last[1] = root[0] = self.__map[key] = [last, root, key]</span><br><span style="color: hsl(0, 100%, 40%);">-        dict_setitem(self, key, value)</span><br><span style="color: hsl(120, 100%, 40%);">+            curr = root[1]</span><br><span style="color: hsl(120, 100%, 40%);">+            while curr is not root:</span><br><span style="color: hsl(120, 100%, 40%);">+                yield curr[2]</span><br><span style="color: hsl(120, 100%, 40%);">+                curr = curr[1]</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def __delitem__(self, key, dict_delitem=dict.__delitem__):</span><br><span style="color: hsl(0, 100%, 40%);">-        'od.__delitem__(y) <==> del od[y]'</span><br><span style="color: hsl(0, 100%, 40%);">-        # Deleting an existing item uses self.__map to find the link which is</span><br><span style="color: hsl(0, 100%, 40%);">-        # then removed by updating the links in the predecessor and successor nodes.</span><br><span style="color: hsl(0, 100%, 40%);">-        dict_delitem(self, key)</span><br><span style="color: hsl(0, 100%, 40%);">-        link_prev, link_next, key = self.__map.pop(key)</span><br><span style="color: hsl(0, 100%, 40%);">-        link_prev[1] = link_next</span><br><span style="color: hsl(0, 100%, 40%);">-        link_next[0] = link_prev</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __iter__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        'od.__iter__() <==> iter(od)'</span><br><span style="color: hsl(0, 100%, 40%);">-        root = self.__root</span><br><span style="color: hsl(0, 100%, 40%);">-        curr = root[1]</span><br><span style="color: hsl(0, 100%, 40%);">-        while curr is not root:</span><br><span style="color: hsl(0, 100%, 40%);">-            yield curr[2]</span><br><span style="color: hsl(0, 100%, 40%);">-            curr = curr[1]</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __reversed__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        'od.__reversed__() <==> reversed(od)'</span><br><span style="color: hsl(0, 100%, 40%);">-        root = self.__root</span><br><span style="color: hsl(0, 100%, 40%);">-        curr = root[0]</span><br><span style="color: hsl(0, 100%, 40%);">-        while curr is not root:</span><br><span style="color: hsl(0, 100%, 40%);">-            yield curr[2]</span><br><span style="color: hsl(0, 100%, 40%);">-            curr = curr[0]</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def clear(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        'od.clear() -> None.  Remove all items from od.'</span><br><span style="color: hsl(0, 100%, 40%);">-        try:</span><br><span style="color: hsl(0, 100%, 40%);">-            for node in self.__map.itervalues():</span><br><span style="color: hsl(0, 100%, 40%);">-                del node[:]</span><br><span style="color: hsl(120, 100%, 40%);">+        def __reversed__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+            'od.__reversed__() <==> reversed(od)'</span><br><span>             root = self.__root</span><br><span style="color: hsl(0, 100%, 40%);">-            root[:] = [root, root, None]</span><br><span style="color: hsl(0, 100%, 40%);">-            self.__map.clear()</span><br><span style="color: hsl(0, 100%, 40%);">-        except AttributeError:</span><br><span style="color: hsl(0, 100%, 40%);">-            pass</span><br><span style="color: hsl(0, 100%, 40%);">-        dict.clear(self)</span><br><span style="color: hsl(120, 100%, 40%);">+            curr = root[0]</span><br><span style="color: hsl(120, 100%, 40%);">+            while curr is not root:</span><br><span style="color: hsl(120, 100%, 40%);">+                yield curr[2]</span><br><span style="color: hsl(120, 100%, 40%);">+                curr = curr[0]</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def popitem(self, last=True):</span><br><span style="color: hsl(0, 100%, 40%);">-        '''od.popitem() -> (k, v), return and remove a (key, value) pair.</span><br><span style="color: hsl(0, 100%, 40%);">-        Pairs are returned in LIFO order if last is true or FIFO order if false.</span><br><span style="color: hsl(120, 100%, 40%);">+        def clear(self):</span><br><span style="color: hsl(120, 100%, 40%);">+            'od.clear() -> None.  Remove all items from od.'</span><br><span style="color: hsl(120, 100%, 40%);">+            try:</span><br><span style="color: hsl(120, 100%, 40%);">+                for node in self.__map.itervalues():</span><br><span style="color: hsl(120, 100%, 40%);">+                    del node[:]</span><br><span style="color: hsl(120, 100%, 40%);">+                root = self.__root</span><br><span style="color: hsl(120, 100%, 40%);">+                root[:] = [root, root, None]</span><br><span style="color: hsl(120, 100%, 40%);">+                self.__map.clear()</span><br><span style="color: hsl(120, 100%, 40%);">+            except AttributeError:</span><br><span style="color: hsl(120, 100%, 40%);">+                pass</span><br><span style="color: hsl(120, 100%, 40%);">+            dict.clear(self)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        '''</span><br><span style="color: hsl(0, 100%, 40%);">-        if not self:</span><br><span style="color: hsl(0, 100%, 40%);">-            raise KeyError('dictionary is empty')</span><br><span style="color: hsl(0, 100%, 40%);">-        root = self.__root</span><br><span style="color: hsl(0, 100%, 40%);">-        if last:</span><br><span style="color: hsl(0, 100%, 40%);">-            link = root[0]</span><br><span style="color: hsl(0, 100%, 40%);">-            link_prev = link[0]</span><br><span style="color: hsl(0, 100%, 40%);">-            link_prev[1] = root</span><br><span style="color: hsl(0, 100%, 40%);">-            root[0] = link_prev</span><br><span style="color: hsl(0, 100%, 40%);">-        else:</span><br><span style="color: hsl(0, 100%, 40%);">-            link = root[1]</span><br><span style="color: hsl(0, 100%, 40%);">-            link_next = link[1]</span><br><span style="color: hsl(0, 100%, 40%);">-            root[1] = link_next</span><br><span style="color: hsl(0, 100%, 40%);">-            link_next[0] = root</span><br><span style="color: hsl(0, 100%, 40%);">-        key = link[2]</span><br><span style="color: hsl(0, 100%, 40%);">-        del self.__map[key]</span><br><span style="color: hsl(0, 100%, 40%);">-        value = dict.pop(self, key)</span><br><span style="color: hsl(0, 100%, 40%);">-        return key, value</span><br><span style="color: hsl(120, 100%, 40%);">+        def popitem(self, last=True):</span><br><span style="color: hsl(120, 100%, 40%);">+            '''od.popitem() -> (k, v), return and remove a (key, value) pair.</span><br><span style="color: hsl(120, 100%, 40%);">+            Pairs are returned in LIFO order if last is true or FIFO order if false.</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    # -- the following methods do not depend on the internal structure --</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def keys(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        'od.keys() -> list of keys in od'</span><br><span style="color: hsl(0, 100%, 40%);">-        return list(self)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def values(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        'od.values() -> list of values in od'</span><br><span style="color: hsl(0, 100%, 40%);">-        return [self[key] for key in self]</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def items(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        'od.items() -> list of (key, value) pairs in od'</span><br><span style="color: hsl(0, 100%, 40%);">-        return [(key, self[key]) for key in self]</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def iterkeys(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        'od.iterkeys() -> an iterator over the keys in od'</span><br><span style="color: hsl(0, 100%, 40%);">-        return iter(self)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def itervalues(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        'od.itervalues -> an iterator over the values in od'</span><br><span style="color: hsl(0, 100%, 40%);">-        for k in self:</span><br><span style="color: hsl(0, 100%, 40%);">-            yield self[k]</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def iteritems(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        'od.iteritems -> an iterator over the (key, value) items in od'</span><br><span style="color: hsl(0, 100%, 40%);">-        for k in self:</span><br><span style="color: hsl(0, 100%, 40%);">-            yield (k, self[k])</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def update(*args, **kwds):</span><br><span style="color: hsl(0, 100%, 40%);">-        '''od.update(E, **F) -> None.  Update od from dict/iterable E and F.</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        If E is a dict instance, does:           for k in E: od[k] = E[k]</span><br><span style="color: hsl(0, 100%, 40%);">-        If E has a .keys() method, does:         for k in E.keys(): od[k] = E[k]</span><br><span style="color: hsl(0, 100%, 40%);">-        Or if E is an iterable of items, does:   for k, v in E: od[k] = v</span><br><span style="color: hsl(0, 100%, 40%);">-        In either case, this is followed by:     for k, v in F.items(): od[k] = v</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        '''</span><br><span style="color: hsl(0, 100%, 40%);">-        if len(args) > 2:</span><br><span style="color: hsl(0, 100%, 40%);">-            raise TypeError('update() takes at most 2 positional '</span><br><span style="color: hsl(0, 100%, 40%);">-                            'arguments (%d given)' % (len(args),))</span><br><span style="color: hsl(0, 100%, 40%);">-        elif not args:</span><br><span style="color: hsl(0, 100%, 40%);">-            raise TypeError('update() takes at least 1 argument (0 given)')</span><br><span style="color: hsl(0, 100%, 40%);">-        self = args[0]</span><br><span style="color: hsl(0, 100%, 40%);">-        # Make progressively weaker assumptions about "other"</span><br><span style="color: hsl(0, 100%, 40%);">-        other = ()</span><br><span style="color: hsl(0, 100%, 40%);">-        if len(args) == 2:</span><br><span style="color: hsl(0, 100%, 40%);">-            other = args[1]</span><br><span style="color: hsl(0, 100%, 40%);">-        if isinstance(other, dict):</span><br><span style="color: hsl(0, 100%, 40%);">-            for key in other:</span><br><span style="color: hsl(0, 100%, 40%);">-                self[key] = other[key]</span><br><span style="color: hsl(0, 100%, 40%);">-        elif hasattr(other, 'keys'):</span><br><span style="color: hsl(0, 100%, 40%);">-            for key in other.keys():</span><br><span style="color: hsl(0, 100%, 40%);">-                self[key] = other[key]</span><br><span style="color: hsl(0, 100%, 40%);">-        else:</span><br><span style="color: hsl(0, 100%, 40%);">-            for key, value in other:</span><br><span style="color: hsl(0, 100%, 40%);">-                self[key] = value</span><br><span style="color: hsl(0, 100%, 40%);">-        for key, value in kwds.items():</span><br><span style="color: hsl(0, 100%, 40%);">-            self[key] = value</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    __update = update  # let subclasses override update without breaking __init__</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    __marker = object()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def pop(self, key, default=__marker):</span><br><span style="color: hsl(0, 100%, 40%);">-        '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.</span><br><span style="color: hsl(0, 100%, 40%);">-        If key is not found, d is returned if given, otherwise KeyError is raised.</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        '''</span><br><span style="color: hsl(0, 100%, 40%);">-        if key in self:</span><br><span style="color: hsl(0, 100%, 40%);">-            result = self[key]</span><br><span style="color: hsl(0, 100%, 40%);">-            del self[key]</span><br><span style="color: hsl(0, 100%, 40%);">-            return result</span><br><span style="color: hsl(0, 100%, 40%);">-        if default is self.__marker:</span><br><span style="color: hsl(0, 100%, 40%);">-            raise KeyError(key)</span><br><span style="color: hsl(0, 100%, 40%);">-        return default</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def setdefault(self, key, default=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'</span><br><span style="color: hsl(0, 100%, 40%);">-        if key in self:</span><br><span style="color: hsl(0, 100%, 40%);">-            return self[key]</span><br><span style="color: hsl(0, 100%, 40%);">-        self[key] = default</span><br><span style="color: hsl(0, 100%, 40%);">-        return default</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __repr__(self, _repr_running={}):</span><br><span style="color: hsl(0, 100%, 40%);">-        'od.__repr__() <==> repr(od)'</span><br><span style="color: hsl(0, 100%, 40%);">-        call_key = id(self), _get_ident()</span><br><span style="color: hsl(0, 100%, 40%);">-        if call_key in _repr_running:</span><br><span style="color: hsl(0, 100%, 40%);">-            return '...'</span><br><span style="color: hsl(0, 100%, 40%);">-        _repr_running[call_key] = 1</span><br><span style="color: hsl(0, 100%, 40%);">-        try:</span><br><span style="color: hsl(120, 100%, 40%);">+            '''</span><br><span>             if not self:</span><br><span style="color: hsl(0, 100%, 40%);">-                return '%s()' % (self.__class__.__name__,)</span><br><span style="color: hsl(0, 100%, 40%);">-            return '%s(%r)' % (self.__class__.__name__, self.items())</span><br><span style="color: hsl(0, 100%, 40%);">-        finally:</span><br><span style="color: hsl(0, 100%, 40%);">-            del _repr_running[call_key]</span><br><span style="color: hsl(120, 100%, 40%);">+                raise KeyError('dictionary is empty')</span><br><span style="color: hsl(120, 100%, 40%);">+            root = self.__root</span><br><span style="color: hsl(120, 100%, 40%);">+            if last:</span><br><span style="color: hsl(120, 100%, 40%);">+                link = root[0]</span><br><span style="color: hsl(120, 100%, 40%);">+                link_prev = link[0]</span><br><span style="color: hsl(120, 100%, 40%);">+                link_prev[1] = root</span><br><span style="color: hsl(120, 100%, 40%);">+                root[0] = link_prev</span><br><span style="color: hsl(120, 100%, 40%);">+            else:</span><br><span style="color: hsl(120, 100%, 40%);">+                link = root[1]</span><br><span style="color: hsl(120, 100%, 40%);">+                link_next = link[1]</span><br><span style="color: hsl(120, 100%, 40%);">+                root[1] = link_next</span><br><span style="color: hsl(120, 100%, 40%);">+                link_next[0] = root</span><br><span style="color: hsl(120, 100%, 40%);">+            key = link[2]</span><br><span style="color: hsl(120, 100%, 40%);">+            del self.__map[key]</span><br><span style="color: hsl(120, 100%, 40%);">+            value = dict.pop(self, key)</span><br><span style="color: hsl(120, 100%, 40%);">+            return key, value</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def __reduce__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        'Return state information for pickling'</span><br><span style="color: hsl(0, 100%, 40%);">-        items = [[k, self[k]] for k in self]</span><br><span style="color: hsl(0, 100%, 40%);">-        inst_dict = vars(self).copy()</span><br><span style="color: hsl(0, 100%, 40%);">-        for k in vars(OrderedDict()):</span><br><span style="color: hsl(0, 100%, 40%);">-            inst_dict.pop(k, None)</span><br><span style="color: hsl(0, 100%, 40%);">-        if inst_dict:</span><br><span style="color: hsl(0, 100%, 40%);">-            return (self.__class__, (items,), inst_dict)</span><br><span style="color: hsl(0, 100%, 40%);">-        return self.__class__, (items,)</span><br><span style="color: hsl(120, 100%, 40%);">+        # -- the following methods do not depend on the internal structure --</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def copy(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        'od.copy() -> a shallow copy of od'</span><br><span style="color: hsl(0, 100%, 40%);">-        return self.__class__(self)</span><br><span style="color: hsl(120, 100%, 40%);">+        def keys(self):</span><br><span style="color: hsl(120, 100%, 40%);">+            'od.keys() -> list of keys in od'</span><br><span style="color: hsl(120, 100%, 40%);">+            return list(self)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    @classmethod</span><br><span style="color: hsl(0, 100%, 40%);">-    def fromkeys(cls, iterable, value=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S</span><br><span style="color: hsl(0, 100%, 40%);">-        and values equal to v (which defaults to None).</span><br><span style="color: hsl(120, 100%, 40%);">+        def values(self):</span><br><span style="color: hsl(120, 100%, 40%);">+            'od.values() -> list of values in od'</span><br><span style="color: hsl(120, 100%, 40%);">+            return [self[key] for key in self]</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        '''</span><br><span style="color: hsl(0, 100%, 40%);">-        d = cls()</span><br><span style="color: hsl(0, 100%, 40%);">-        for key in iterable:</span><br><span style="color: hsl(0, 100%, 40%);">-            d[key] = value</span><br><span style="color: hsl(0, 100%, 40%);">-        return d</span><br><span style="color: hsl(120, 100%, 40%);">+        def items(self):</span><br><span style="color: hsl(120, 100%, 40%);">+            'od.items() -> list of (key, value) pairs in od'</span><br><span style="color: hsl(120, 100%, 40%);">+            return [(key, self[key]) for key in self]</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def __eq__(self, other):</span><br><span style="color: hsl(0, 100%, 40%);">-        '''od.__eq__(y) <==> od==y.  Comparison to another OD is order-sensitive</span><br><span style="color: hsl(0, 100%, 40%);">-        while comparison to a regular mapping is order-insensitive.</span><br><span style="color: hsl(120, 100%, 40%);">+        def iterkeys(self):</span><br><span style="color: hsl(120, 100%, 40%);">+            'od.iterkeys() -> an iterator over the keys in od'</span><br><span style="color: hsl(120, 100%, 40%);">+            return iter(self)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        '''</span><br><span style="color: hsl(0, 100%, 40%);">-        if isinstance(other, OrderedDict):</span><br><span style="color: hsl(0, 100%, 40%);">-            return len(self)==len(other) and self.items() == other.items()</span><br><span style="color: hsl(0, 100%, 40%);">-        return dict.__eq__(self, other)</span><br><span style="color: hsl(120, 100%, 40%);">+        def itervalues(self):</span><br><span style="color: hsl(120, 100%, 40%);">+            'od.itervalues -> an iterator over the values in od'</span><br><span style="color: hsl(120, 100%, 40%);">+            for k in self:</span><br><span style="color: hsl(120, 100%, 40%);">+                yield self[k]</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def __ne__(self, other):</span><br><span style="color: hsl(0, 100%, 40%);">-        return not self == other</span><br><span style="color: hsl(120, 100%, 40%);">+        def iteritems(self):</span><br><span style="color: hsl(120, 100%, 40%);">+            'od.iteritems -> an iterator over the (key, value) items in od'</span><br><span style="color: hsl(120, 100%, 40%);">+            for k in self:</span><br><span style="color: hsl(120, 100%, 40%);">+                yield (k, self[k])</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    # -- the following methods are only used in Python 2.7 --</span><br><span style="color: hsl(120, 100%, 40%);">+        def update(*args, **kwds):</span><br><span style="color: hsl(120, 100%, 40%);">+            '''od.update(E, **F) -> None.  Update od from dict/iterable E and F.</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def viewkeys(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        "od.viewkeys() -> a set-like object providing a view on od's keys"</span><br><span style="color: hsl(0, 100%, 40%);">-        return KeysView(self)</span><br><span style="color: hsl(120, 100%, 40%);">+            If E is a dict instance, does:           for k in E: od[k] = E[k]</span><br><span style="color: hsl(120, 100%, 40%);">+            If E has a .keys() method, does:         for k in E.keys(): od[k] = E[k]</span><br><span style="color: hsl(120, 100%, 40%);">+            Or if E is an iterable of items, does:   for k, v in E: od[k] = v</span><br><span style="color: hsl(120, 100%, 40%);">+            In either case, this is followed by:     for k, v in F.items(): od[k] = v</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def viewvalues(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        "od.viewvalues() -> an object providing a view on od's values"</span><br><span style="color: hsl(0, 100%, 40%);">-        return ValuesView(self)</span><br><span style="color: hsl(120, 100%, 40%);">+            '''</span><br><span style="color: hsl(120, 100%, 40%);">+            if len(args) > 2:</span><br><span style="color: hsl(120, 100%, 40%);">+                raise TypeError('update() takes at most 2 positional '</span><br><span style="color: hsl(120, 100%, 40%);">+                                'arguments (%d given)' % (len(args),))</span><br><span style="color: hsl(120, 100%, 40%);">+            elif not args:</span><br><span style="color: hsl(120, 100%, 40%);">+                raise TypeError('update() takes at least 1 argument (0 given)')</span><br><span style="color: hsl(120, 100%, 40%);">+            self = args[0]</span><br><span style="color: hsl(120, 100%, 40%);">+            # Make progressively weaker assumptions about "other"</span><br><span style="color: hsl(120, 100%, 40%);">+            other = ()</span><br><span style="color: hsl(120, 100%, 40%);">+            if len(args) == 2:</span><br><span style="color: hsl(120, 100%, 40%);">+                other = args[1]</span><br><span style="color: hsl(120, 100%, 40%);">+            if isinstance(other, dict):</span><br><span style="color: hsl(120, 100%, 40%);">+                for key in other:</span><br><span style="color: hsl(120, 100%, 40%);">+                    self[key] = other[key]</span><br><span style="color: hsl(120, 100%, 40%);">+            elif hasattr(other, 'keys'):</span><br><span style="color: hsl(120, 100%, 40%);">+                for key in other.keys():</span><br><span style="color: hsl(120, 100%, 40%);">+                    self[key] = other[key]</span><br><span style="color: hsl(120, 100%, 40%);">+            else:</span><br><span style="color: hsl(120, 100%, 40%);">+                for key, value in other:</span><br><span style="color: hsl(120, 100%, 40%);">+                    self[key] = value</span><br><span style="color: hsl(120, 100%, 40%);">+            for key, value in kwds.items():</span><br><span style="color: hsl(120, 100%, 40%);">+                self[key] = value</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def viewitems(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        "od.viewitems() -> a set-like object providing a view on od's items"</span><br><span style="color: hsl(0, 100%, 40%);">-        return ItemsView(self)</span><br><span style="color: hsl(120, 100%, 40%);">+        __update = update  # let subclasses override update without breaking __init__</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        __marker = object()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def pop(self, key, default=__marker):</span><br><span style="color: hsl(120, 100%, 40%);">+            '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.</span><br><span style="color: hsl(120, 100%, 40%);">+            If key is not found, d is returned if given, otherwise KeyError is raised.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            '''</span><br><span style="color: hsl(120, 100%, 40%);">+            if key in self:</span><br><span style="color: hsl(120, 100%, 40%);">+                result = self[key]</span><br><span style="color: hsl(120, 100%, 40%);">+                del self[key]</span><br><span style="color: hsl(120, 100%, 40%);">+                return result</span><br><span style="color: hsl(120, 100%, 40%);">+            if default is self.__marker:</span><br><span style="color: hsl(120, 100%, 40%);">+                raise KeyError(key)</span><br><span style="color: hsl(120, 100%, 40%);">+            return default</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def setdefault(self, key, default=None):</span><br><span style="color: hsl(120, 100%, 40%);">+            'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'</span><br><span style="color: hsl(120, 100%, 40%);">+            if key in self:</span><br><span style="color: hsl(120, 100%, 40%);">+                return self[key]</span><br><span style="color: hsl(120, 100%, 40%);">+            self[key] = default</span><br><span style="color: hsl(120, 100%, 40%);">+            return default</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def __repr__(self, _repr_running={}):</span><br><span style="color: hsl(120, 100%, 40%);">+            'od.__repr__() <==> repr(od)'</span><br><span style="color: hsl(120, 100%, 40%);">+            call_key = id(self), _get_ident()</span><br><span style="color: hsl(120, 100%, 40%);">+            if call_key in _repr_running:</span><br><span style="color: hsl(120, 100%, 40%);">+                return '...'</span><br><span style="color: hsl(120, 100%, 40%);">+            _repr_running[call_key] = 1</span><br><span style="color: hsl(120, 100%, 40%);">+            try:</span><br><span style="color: hsl(120, 100%, 40%);">+                if not self:</span><br><span style="color: hsl(120, 100%, 40%);">+                    return '%s()' % (self.__class__.__name__,)</span><br><span style="color: hsl(120, 100%, 40%);">+                return '%s(%r)' % (self.__class__.__name__, self.items())</span><br><span style="color: hsl(120, 100%, 40%);">+            finally:</span><br><span style="color: hsl(120, 100%, 40%);">+                del _repr_running[call_key]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def __reduce__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+            'Return state information for pickling'</span><br><span style="color: hsl(120, 100%, 40%);">+            items = [[k, self[k]] for k in self]</span><br><span style="color: hsl(120, 100%, 40%);">+            inst_dict = vars(self).copy()</span><br><span style="color: hsl(120, 100%, 40%);">+            for k in vars(OrderedDict()):</span><br><span style="color: hsl(120, 100%, 40%);">+                inst_dict.pop(k, None)</span><br><span style="color: hsl(120, 100%, 40%);">+            if inst_dict:</span><br><span style="color: hsl(120, 100%, 40%);">+                return (self.__class__, (items,), inst_dict)</span><br><span style="color: hsl(120, 100%, 40%);">+            return self.__class__, (items,)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def copy(self):</span><br><span style="color: hsl(120, 100%, 40%);">+            'od.copy() -> a shallow copy of od'</span><br><span style="color: hsl(120, 100%, 40%);">+            return self.__class__(self)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        @classmethod</span><br><span style="color: hsl(120, 100%, 40%);">+        def fromkeys(cls, iterable, value=None):</span><br><span style="color: hsl(120, 100%, 40%);">+            '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S</span><br><span style="color: hsl(120, 100%, 40%);">+            and values equal to v (which defaults to None).</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            '''</span><br><span style="color: hsl(120, 100%, 40%);">+            d = cls()</span><br><span style="color: hsl(120, 100%, 40%);">+            for key in iterable:</span><br><span style="color: hsl(120, 100%, 40%);">+                d[key] = value</span><br><span style="color: hsl(120, 100%, 40%);">+            return d</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def __eq__(self, other):</span><br><span style="color: hsl(120, 100%, 40%);">+            '''od.__eq__(y) <==> od==y.  Comparison to another OD is order-sensitive</span><br><span style="color: hsl(120, 100%, 40%);">+            while comparison to a regular mapping is order-insensitive.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            '''</span><br><span style="color: hsl(120, 100%, 40%);">+            if isinstance(other, OrderedDict):</span><br><span style="color: hsl(120, 100%, 40%);">+                return len(self)==len(other) and self.items() == other.items()</span><br><span style="color: hsl(120, 100%, 40%);">+            return dict.__eq__(self, other)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def __ne__(self, other):</span><br><span style="color: hsl(120, 100%, 40%);">+            return not self == other</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        # -- the following methods are only used in Python 2.7 --</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def viewkeys(self):</span><br><span style="color: hsl(120, 100%, 40%);">+            "od.viewkeys() -> a set-like object providing a view on od's keys"</span><br><span style="color: hsl(120, 100%, 40%);">+            return KeysView(self)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def viewvalues(self):</span><br><span style="color: hsl(120, 100%, 40%);">+            "od.viewvalues() -> an object providing a view on od's values"</span><br><span style="color: hsl(120, 100%, 40%);">+            return ValuesView(self)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        def viewitems(self):</span><br><span style="color: hsl(120, 100%, 40%);">+            "od.viewitems() -> a set-like object providing a view on od's items"</span><br><span style="color: hsl(120, 100%, 40%);">+            return ItemsView(self)</span><br><span> </span><br><span> ###############################################################################</span><br><span> ### MultiOrderedDict</span><br><span>@@ -292,7 +296,7 @@</span><br><span>         # TODO - find out why for some reason copies</span><br><span>         #        the [] as an [[]], so do manually</span><br><span>         c = MultiOrderedDict() #self.__class__(self)</span><br><span style="color: hsl(0, 100%, 40%);">-        for key, val in self.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+        for key, val in self.items():</span><br><span>             for v in val:</span><br><span>                 c[key] = v</span><br><span>         return c</span><br><span>diff --git a/lib/python/asterisk/asterisk.py b/lib/python/asterisk/asterisk.py</span><br><span>old mode 100755</span><br><span>new mode 100644</span><br><span>index 985afea..c3996f6</span><br><span>--- a/lib/python/asterisk/asterisk.py</span><br><span>+++ b/lib/python/asterisk/asterisk.py</span><br><span>@@ -1,4 +1,3 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python</span><br><span> """Asterisk Instances in Python.</span><br><span> </span><br><span> This module provides an interface for creating instances of Asterisk</span><br><span>@@ -18,9 +17,9 @@</span><br><span> import logging</span><br><span> import fileinput</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-import test_suite_utils</span><br><span style="color: hsl(120, 100%, 40%);">+from . import test_suite_utils</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from config import ConfigFile</span><br><span style="color: hsl(120, 100%, 40%);">+from .config import ConfigFile</span><br><span> </span><br><span> from twisted.internet import reactor, protocol, defer, utils, error</span><br><span> from twisted.python.failure import Failure</span><br><span>@@ -54,7 +53,7 @@</span><br><span>     def dataReceived(self, data):</span><br><span>         """Called when we receive data back"""</span><br><span>         LOGGER.debug(data)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.output += data</span><br><span style="color: hsl(120, 100%, 40%);">+        self.output += data.decode('utf-8', 'ignore')</span><br><span> </span><br><span>     def connectionLost(self, reason):</span><br><span>         """Called when the connect is lost"""</span><br><span>@@ -206,7 +205,8 @@</span><br><span>     def _set_properties(self, result):</span><br><span>         """Set the properties based on the result of the</span><br><span>         getProcessOutputAndValue call"""</span><br><span style="color: hsl(0, 100%, 40%);">-        self.output, self.err, self.exitcode = result</span><br><span style="color: hsl(120, 100%, 40%);">+        bintxt, self.err, self.exitcode = result</span><br><span style="color: hsl(120, 100%, 40%);">+        self.output = bintxt.decode('utf-8', 'ignore')</span><br><span> </span><br><span> </span><br><span> class AsteriskProtocol(protocol.ProcessProtocol):</span><br><span>@@ -232,7 +232,7 @@</span><br><span> </span><br><span>     def outReceived(self, data):</span><br><span>         """Override of ProcessProtocol.outReceived"""</span><br><span style="color: hsl(0, 100%, 40%);">-        self.output += data</span><br><span style="color: hsl(120, 100%, 40%);">+        self.output += data.decode('utf-8', 'ignore')</span><br><span> </span><br><span>     def connectionMade(self):</span><br><span>         """Override of ProcessProtocol.connectionMade"""</span><br><span>@@ -311,20 +311,6 @@</span><br><span>     we're not running into the asterisk.ctl AF_UNIX limit.</span><br><span>     """</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def compare_free_space(x, y):</span><br><span style="color: hsl(0, 100%, 40%);">-        if os.stat(y).st_dev == os.stat(x).st_dev:</span><br><span style="color: hsl(0, 100%, 40%);">-            return 0</span><br><span style="color: hsl(0, 100%, 40%);">-        # statvfs can return a long; comparison functions must return an</span><br><span style="color: hsl(0, 100%, 40%);">-        # int.  Where both are same filesystem, bavail might change from</span><br><span style="color: hsl(0, 100%, 40%);">-        # one call to the next, but hopefully it is not more than 1000.</span><br><span style="color: hsl(0, 100%, 40%);">-        difference = os.statvfs(y).f_bavail - os.statvfs(x).f_bavail</span><br><span style="color: hsl(0, 100%, 40%);">-        if (difference > 1000):</span><br><span style="color: hsl(0, 100%, 40%);">-            return 1</span><br><span style="color: hsl(0, 100%, 40%);">-        elif (difference < 1000):</span><br><span style="color: hsl(0, 100%, 40%);">-            return -1</span><br><span style="color: hsl(0, 100%, 40%);">-        else:</span><br><span style="color: hsl(0, 100%, 40%);">-            return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>     localtest_root = os.getenv("AST_TEST_ROOT")</span><br><span>     if localtest_root:</span><br><span>         # Base location of the temporary files created by the testsuite</span><br><span>@@ -333,7 +319,7 @@</span><br><span>         default_etc_directory = os.path.join(localtest_root, "etc/asterisk")</span><br><span>     else:</span><br><span>         # select tmp path with most available space</span><br><span style="color: hsl(0, 100%, 40%);">-        best_tmp = sorted(['/tmp', '/var/tmp'], cmp=compare_free_space)[0]</span><br><span style="color: hsl(120, 100%, 40%);">+        best_tmp = sorted(['/tmp', '/var/tmp'], key=lambda path: os.statvfs(path).f_bavail)[0]</span><br><span>         # Base location of the temporary files created by the testsuite</span><br><span>         test_suite_root = best_tmp + "/asterisk-testsuite"</span><br><span>         # The default etc directory for Asterisk</span><br><span>@@ -955,7 +941,7 @@</span><br><span>                 ast_file.write("#include \"%s/asterisk.options.conf.inc\"\n" %</span><br><span>                                (self.astetcdir))</span><br><span>                 if ast_conf_options:</span><br><span style="color: hsl(0, 100%, 40%);">-                    for (var, val) in ast_conf_options.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+                    for (var, val) in ast_conf_options.items():</span><br><span>                         ast_file.write("%s = %s\n" % (var, val))</span><br><span>                 for (var, val) in cat.options:</span><br><span>                     if not ast_conf_options or var not in ast_conf_options:</span><br><span>diff --git a/lib/python/asterisk/bridge_test_case.py b/lib/python/asterisk/bridge_test_case.py</span><br><span>index ea1767e..155b3a1 100644</span><br><span>--- a/lib/python/asterisk/bridge_test_case.py</span><br><span>+++ b/lib/python/asterisk/bridge_test_case.py</span><br><span>@@ -13,7 +13,7 @@</span><br><span> from time import sleep</span><br><span> </span><br><span> sys.path.append("lib/python")</span><br><span style="color: hsl(0, 100%, 40%);">-from test_case import TestCase</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_case import TestCase</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>diff --git a/lib/python/asterisk/buildoptions.py b/lib/python/asterisk/buildoptions.py</span><br><span>index 3b0b4b9..833053a 100644</span><br><span>--- a/lib/python/asterisk/buildoptions.py</span><br><span>+++ b/lib/python/asterisk/buildoptions.py</span><br><span>@@ -1,4 +1,3 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python</span><br><span> """Asterisk Build Options Handling</span><br><span> </span><br><span> This module implements an Asterisk build options parser.  It</span><br><span>@@ -13,7 +12,6 @@</span><br><span> """</span><br><span> </span><br><span> import sys</span><br><span style="color: hsl(0, 100%, 40%);">-import unittest</span><br><span> </span><br><span> </span><br><span> class AsteriskBuildOptions(object):</span><br><span>@@ -51,7 +49,7 @@</span><br><span>         except IOError:</span><br><span>             return ret_val</span><br><span>         except:</span><br><span style="color: hsl(0, 100%, 40%);">-            print "Unexpected error: %s" % sys.exc_info()[0]</span><br><span style="color: hsl(120, 100%, 40%);">+            print("Unexpected error: %s" % sys.exc_info()[0])</span><br><span>             return ret_val</span><br><span>         for line in file_lines:</span><br><span>             if "#define" in line:</span><br><span>@@ -59,7 +57,7 @@</span><br><span>                 if (define_tuple[0] == "" or define_tuple[2] == ""):</span><br><span>                     msg = ("Unable to parse build option line [%s] into "</span><br><span>                            "compiler flag token and value" % line)</span><br><span style="color: hsl(0, 100%, 40%);">-                    print msg</span><br><span style="color: hsl(120, 100%, 40%);">+                    print(msg)</span><br><span>                 else:</span><br><span>                     flag = define_tuple[0].strip()</span><br><span>                     allowed = define_tuple[2].strip()</span><br><span>@@ -89,22 +87,3 @@</span><br><span>         elif expected_value == "0":</span><br><span>             return True</span><br><span>         return False</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class AsteriskBuildOptionsTests(unittest.TestCase):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Unit tests for AsteriskBuildOptions"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_1(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Test the defaults paths"""</span><br><span style="color: hsl(0, 100%, 40%);">-        build_options = AsteriskBuildOptions()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(1)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-def main():</span><br><span style="color: hsl(0, 100%, 40%);">-    """Main entry point for unit tests"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    unittest.main()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-if __name__ == "__main__":</span><br><span style="color: hsl(0, 100%, 40%);">-    main()</span><br><span>diff --git a/lib/python/asterisk/cdr.py b/lib/python/asterisk/cdr.py</span><br><span>index cc5226b..c5d62db 100644</span><br><span>--- a/lib/python/asterisk/cdr.py</span><br><span>+++ b/lib/python/asterisk/cdr.py</span><br><span>@@ -1,4 +1,3 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python</span><br><span> """Asterisk call detail record testing</span><br><span> </span><br><span> This module implements an Asterisk CDR parser.</span><br><span>@@ -10,9 +9,8 @@</span><br><span> the GNU General Public License Version 2.</span><br><span> """</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-import unittest</span><br><span> import sys</span><br><span style="color: hsl(0, 100%, 40%);">-import astcsv</span><br><span style="color: hsl(120, 100%, 40%);">+from . import astcsv</span><br><span> import logging</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span>@@ -138,32 +136,4 @@</span><br><span>             AsteriskCSVCDRLine.fields, AsteriskCSVCDRLine)</span><br><span> </span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-class AsteriskCSVCDRTests(unittest.TestCase):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Unit tests for AsteriskCSVCDR"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cdr(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Test the self_test/Master.csv record"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        cdr = AsteriskCSVCDR("self_test/Master.csv")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(len(cdr), 2)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(</span><br><span style="color: hsl(0, 100%, 40%);">-            AsteriskCSVCDRLine(duration=7, lastapp="hangup").match(</span><br><span style="color: hsl(0, 100%, 40%);">-                cdr[0],</span><br><span style="color: hsl(0, 100%, 40%);">-                exact=(True, True)))</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(cdr[0].match(</span><br><span style="color: hsl(0, 100%, 40%);">-            AsteriskCSVCDRLine(duration=7, lastapp="hangup"),</span><br><span style="color: hsl(0, 100%, 40%);">-            exact=(True, True)))</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(cdr[1].match(cdr[0]))</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(cdr[0].match(cdr[1]))</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(cdr[0].billsec, "7")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(cdr.match(cdr))</span><br><span style="color: hsl(0, 100%, 40%);">-        cdr2 = AsteriskCSVCDR("self_test/Master2.csv")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(cdr.match(cdr2))</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-if __name__ == '__main__':</span><br><span style="color: hsl(0, 100%, 40%);">-    unittest.main()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> # vim:sw=4:ts=4:expandtab:textwidth=79</span><br><span>diff --git a/lib/python/asterisk/cel.py b/lib/python/asterisk/cel.py</span><br><span>index d22d67f..9965270 100644</span><br><span>--- a/lib/python/asterisk/cel.py</span><br><span>+++ b/lib/python/asterisk/cel.py</span><br><span>@@ -1,4 +1,3 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python</span><br><span> """Asterisk call detail record testing</span><br><span> </span><br><span> This module implements an Asterisk CEL parser.</span><br><span>@@ -11,8 +10,7 @@</span><br><span> """</span><br><span> </span><br><span> import yaml</span><br><span style="color: hsl(0, 100%, 40%);">-import unittest</span><br><span style="color: hsl(0, 100%, 40%);">-import astcsv</span><br><span style="color: hsl(120, 100%, 40%);">+from . import astcsv</span><br><span> import re</span><br><span> import logging</span><br><span> </span><br><span>@@ -177,36 +175,4 @@</span><br><span>             AsteriskCSVCELLine.fields, AsteriskCSVCELLine)</span><br><span> </span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-class AsteriskCSVCELTests(unittest.TestCase):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Unit tests for AsteriskCSVCEL"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cel(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Test CEL using self_test/CELMaster1.csv"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        cel = AsteriskCSVCEL("self_test/CELMaster1.csv")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(len(cel), 16)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(AsteriskCSVCELLine(</span><br><span style="color: hsl(0, 100%, 40%);">-            eventtype="LINKEDID_END",</span><br><span style="color: hsl(0, 100%, 40%);">-            channel="TinCan/string").match(cel[-1],</span><br><span style="color: hsl(0, 100%, 40%);">-                                           silent=True,</span><br><span style="color: hsl(0, 100%, 40%);">-                                           exact=(True, True)))</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(cel[-1].match(</span><br><span style="color: hsl(0, 100%, 40%);">-            AsteriskCSVCELLine(eventtype="LINKEDID_END",</span><br><span style="color: hsl(0, 100%, 40%);">-                               channel="TinCan/string"),</span><br><span style="color: hsl(0, 100%, 40%);">-            silent=True,</span><br><span style="color: hsl(0, 100%, 40%);">-            exact=(True, True)))</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(cel[1].match(cel[0], silent=True))</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(cel[0].match(cel[1], silent=True))</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(cel[-1].channel, "TinCan/string")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(cel.match(cel))</span><br><span style="color: hsl(0, 100%, 40%);">-        cel2 = AsteriskCSVCEL("self_test/CELMaster2.csv")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(cel.match(cel2))</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-if __name__ == '__main__':</span><br><span style="color: hsl(0, 100%, 40%);">-    logging.basicConfig()</span><br><span style="color: hsl(0, 100%, 40%);">-    unittest.main()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> # vim:sw=4:ts=4:expandtab:textwidth=79</span><br><span>diff --git a/lib/python/asterisk/channel_test_condition.py b/lib/python/asterisk/channel_test_condition.py</span><br><span>index ec1d774..36421a4 100644</span><br><span>--- a/lib/python/asterisk/channel_test_condition.py</span><br><span>+++ b/lib/python/asterisk/channel_test_condition.py</span><br><span>@@ -1,4 +1,3 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python</span><br><span> """Test condition for channels</span><br><span> </span><br><span> Copyright (C) 2011-2012, Digium, Inc.</span><br><span>@@ -9,9 +8,7 @@</span><br><span> """</span><br><span> </span><br><span> from twisted.internet import defer</span><br><span style="color: hsl(0, 100%, 40%);">-from test_conditions import TestCondition</span><br><span style="color: hsl(0, 100%, 40%);">-import logging</span><br><span style="color: hsl(0, 100%, 40%);">-import unittest</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_conditions import TestCondition</span><br><span> import re</span><br><span> </span><br><span> </span><br><span>@@ -86,155 +83,3 @@</span><br><span>         defer.DeferredList(exec_list).addCallback(_raise_finished,</span><br><span>                                                   finish_deferred)</span><br><span>         return finish_deferred</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class AstMockOutput(object):</span><br><span style="color: hsl(0, 100%, 40%);">-    """mock cli output base class"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Constructor"""</span><br><span style="color: hsl(0, 100%, 40%);">-        self.host = "127.0.0.1"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def MockDefer(self, output):</span><br><span style="color: hsl(0, 100%, 40%);">-        """use real defer to mock deferred output"""</span><br><span style="color: hsl(0, 100%, 40%);">-        self.output = output</span><br><span style="color: hsl(0, 100%, 40%);">-        deferred = defer.Deferred()</span><br><span style="color: hsl(0, 100%, 40%);">-        deferred.callback(self)</span><br><span style="color: hsl(0, 100%, 40%);">-        return deferred</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class AstMockObjectInactive(AstMockOutput):</span><br><span style="color: hsl(0, 100%, 40%);">-    """mock cli output showing no active channels"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def cli_exec(self, command):</span><br><span style="color: hsl(0, 100%, 40%);">-        """presume command is core show channels and generate output"""</span><br><span style="color: hsl(0, 100%, 40%);">-        output = ""</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "Channel              Location             State   Application(Data)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "0 active channels\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "0 active calls\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "2 calls processed\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "Asterisk ending (0).\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        return self.MockDefer(output)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class AstMockObjectSingle(AstMockOutput):</span><br><span style="color: hsl(0, 100%, 40%);">-    """mock cli output showing single active channel"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def cli_exec(self, command):</span><br><span style="color: hsl(0, 100%, 40%);">-        """presume command is core show channels and generate output"""</span><br><span style="color: hsl(0, 100%, 40%);">-        output = ""</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "Channel              Location             State   Application(Data)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "Local/123@default-00 (None)               Down    ()\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "1 active channels\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "0 active calls\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "2 calls processed\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "Asterisk ending (0).\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        return self.MockDefer(output)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class AstMockObjectMultiple(AstMockOutput):</span><br><span style="color: hsl(0, 100%, 40%);">-    """mock cli output showing multiple active channels"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def cli_exec(self, command):</span><br><span style="color: hsl(0, 100%, 40%);">-        """presume command is core show channels and generate output"""</span><br><span style="color: hsl(0, 100%, 40%);">-        output = ""</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "Channel              Location             State   Application(Data)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "PJSIP/123@default-00 (None)               Down    ()\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "Local/123@default-00 (None)               Down    ()\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "SIP/alice@default-00 (None)               Down    ()\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "3 active channels\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "0 active calls\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "2 calls processed\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "Asterisk ending (0).\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        return self.MockDefer(output)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class AstMockObjectLeaked(AstMockOutput):</span><br><span style="color: hsl(0, 100%, 40%);">-    """mock cli output showing leaked channel"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def cli_exec(self, command):</span><br><span style="color: hsl(0, 100%, 40%);">-        """presume command is core show channels and generate output"""</span><br><span style="color: hsl(0, 100%, 40%);">-        output = ""</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "Channel              Location             State   Application(Data)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "Local/123@default-00 (None)               Down    ()\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "0 active channels\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "0 active calls\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "2 calls processed\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        output += "Asterisk ending (0).\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        return self.MockDefer(output)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class TestConfig(object):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Fake TestConfig object for unittest"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        self.class_type_name = "bogus"</span><br><span style="color: hsl(0, 100%, 40%);">-        self.config = {}</span><br><span style="color: hsl(0, 100%, 40%);">-        self.enabled = True</span><br><span style="color: hsl(0, 100%, 40%);">-        self.pass_expected = True</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class ChannelTestConditionUnitTest(unittest.TestCase):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Unit Tests for ChannelTestCondition"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_evaluate_inactive(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """test inactive channel condition"""</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = ChannelTestCondition(TestConfig())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(AstMockObjectInactive())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Passed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_evaluate_multiple_fail(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """test multiple channel condition"""</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = ChannelTestCondition(TestConfig())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(AstMockObjectMultiple())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_evaluate_multiple_fail2(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """test multiple channel condition"""</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = ChannelTestCondition(TestConfig())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.allowed_channels = 2</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(AstMockObjectMultiple())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_evaluate_multiple_pass(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """test multiple channel condition"""</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = ChannelTestCondition(TestConfig())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.allowed_channels = 3</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(AstMockObjectMultiple())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Passed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_evaluate_single_fail(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """test single channel condition"""</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = ChannelTestCondition(TestConfig())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(AstMockObjectSingle())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_evaluate_single_pass(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """test single channel condition"""</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = ChannelTestCondition(TestConfig())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.allowed_channels = 1</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(AstMockObjectSingle())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Passed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_evaluate_leaked(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """test leaked channel condition"""</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = ChannelTestCondition(TestConfig())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(AstMockObjectLeaked())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-def main():</span><br><span style="color: hsl(0, 100%, 40%);">-    """Run the unit tests"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    logging.basicConfig(level=logging.DEBUG)</span><br><span style="color: hsl(0, 100%, 40%);">-    unittest.main()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-if __name__ == "__main__":</span><br><span style="color: hsl(0, 100%, 40%);">-    main()</span><br><span>diff --git a/lib/python/asterisk/confbridge.py b/lib/python/asterisk/confbridge.py</span><br><span>index acca1e7..0ca364d 100644</span><br><span>--- a/lib/python/asterisk/confbridge.py</span><br><span>+++ b/lib/python/asterisk/confbridge.py</span><br><span>@@ -14,7 +14,7 @@</span><br><span> import sys</span><br><span> import logging</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from test_state import TestState</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_state import TestState</span><br><span> from twisted.internet import reactor</span><br><span> </span><br><span> sys.path.append("lib/python")</span><br><span>diff --git a/lib/python/asterisk/config.py b/lib/python/asterisk/config.py</span><br><span>old mode 100755</span><br><span>new mode 100644</span><br><span>index e26b156..c5662d2</span><br><span>--- a/lib/python/asterisk/config.py</span><br><span>+++ b/lib/python/asterisk/config.py</span><br><span>@@ -1,4 +1,3 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python</span><br><span> """Asterisk Configuration File Handling.</span><br><span> </span><br><span> This module implements interfaces for dealing with Asterisk configuration</span><br><span>@@ -18,7 +17,6 @@</span><br><span> </span><br><span> import sys</span><br><span> import re</span><br><span style="color: hsl(0, 100%, 40%);">-import unittest</span><br><span> import logging</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span>@@ -54,7 +52,7 @@</span><br><span>         match = self.varval_re.match(line)</span><br><span>         if match is None:</span><br><span>             if not is_blank_line(line):</span><br><span style="color: hsl(0, 100%, 40%);">-                LOGGER.warn("Invalid line: '%s'" % line.strip())</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGGER.warning("Invalid line: '%s'" % line.strip())</span><br><span>             return</span><br><span>         self.options.append((match.group("name"), match.group("value").strip()))</span><br><span> </span><br><span>@@ -109,94 +107,6 @@</span><br><span>             )</span><br><span>         elif len(self.categories) == 0:</span><br><span>             if not is_blank_line(line):</span><br><span style="color: hsl(0, 100%, 40%);">-                LOGGER.warn("Invalid line: '%s'" % line.strip())</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGGER.warning("Invalid line: '%s'" % line.strip())</span><br><span>         else:</span><br><span>             self.categories[-1].parse_line(line)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class ConfigFileTests(unittest.TestCase):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Unit tests for ConfigFile"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_conf(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Test parsing a blob of config data"""</span><br><span style="color: hsl(0, 100%, 40%);">-        test = \</span><br><span style="color: hsl(0, 100%, 40%);">-            "; stuff\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "this line is invalid on purpose\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "[this is] also invalid]\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            ";-- comment --;\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            ";--   \n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "[this is commented out]\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "         --;\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "[foo]\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "a = b\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "  b =   a  \n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "this line is invalid on purpose\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            ";moo\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "c = d;asdadf;adfads;adsfasdf\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "  [bar]   ;asdfasdf\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "a-b=c-d\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "xyz=x|y|z\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "1234 => 4242,Example Mailbox,root@localhost,,var=val\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "[template](!)\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "foo=bar\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "exten => _NXX.,n,Wait(1)\n" \</span><br><span style="color: hsl(0, 100%, 40%);">-            "astetcdir => /etc/asterisk\n"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        conf = ConfigFile(fn=None, config_str=test)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(len(conf.categories), 3)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[0].name, "foo")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(conf.categories[0].template)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(len(conf.categories[0].options), 3)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[0].options[0][0], "a")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[0].options[0][1], "b")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[0].options[1][0], "b")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[0].options[1][1], "a")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[0].options[2][0], "c")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[0].options[2][1], "d")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[1].name, "bar")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(conf.categories[1].template)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(len(conf.categories[1].options), 3)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[1].options[0][0], "a-b")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[1].options[0][1], "c-d")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[1].options[1][0], "xyz")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[1].options[1][1], "x|y|z")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[1].options[2][0], "1234")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[1].options[2][1],</span><br><span style="color: hsl(0, 100%, 40%);">-                         "4242,Example Mailbox,root@localhost,,var=val")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[2].name, "template")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(conf.categories[2].template)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(len(conf.categories[2].options), 3)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[2].options[0][0], "foo")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[2].options[0][1], "bar")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[2].options[1][0], "exten")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[2].options[1][1],</span><br><span style="color: hsl(0, 100%, 40%);">-                         "_NXX.,n,Wait(1)")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[2].options[2][0], "astetcdir")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(conf.categories[2].options[2][1], "/etc/asterisk")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-def main(argv=None):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Read in and show a config file, or run unit tests"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    if argv is None:</span><br><span style="color: hsl(0, 100%, 40%);">-        argv = sys.argv</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    if len(argv) == 2:</span><br><span style="color: hsl(0, 100%, 40%);">-        conf = ConfigFile(argv[1])</span><br><span style="color: hsl(0, 100%, 40%);">-        for cat in conf.categories:</span><br><span style="color: hsl(0, 100%, 40%);">-            LOGGER.debug("[%s]" % cat.name)</span><br><span style="color: hsl(0, 100%, 40%);">-            for (var, val) in cat.options:</span><br><span style="color: hsl(0, 100%, 40%);">-                LOGGER.debug("%s = %s" % (var, val))</span><br><span style="color: hsl(0, 100%, 40%);">-    else:</span><br><span style="color: hsl(0, 100%, 40%);">-        return unittest.main()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    return 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-if __name__ == "__main__":</span><br><span style="color: hsl(0, 100%, 40%);">-    sys.exit(main() or 0)</span><br><span>diff --git a/lib/python/asterisk/fd_test_condition.py b/lib/python/asterisk/fd_test_condition.py</span><br><span>index 4a8f869..234af28 100644</span><br><span>--- a/lib/python/asterisk/fd_test_condition.py</span><br><span>+++ b/lib/python/asterisk/fd_test_condition.py</span><br><span>@@ -11,7 +11,7 @@</span><br><span> import logging</span><br><span> </span><br><span> from twisted.internet import defer</span><br><span style="color: hsl(0, 100%, 40%);">-from test_conditions import TestCondition</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_conditions import TestCondition</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>diff --git a/lib/python/asterisk/lock_test_condition.py b/lib/python/asterisk/lock_test_condition.py</span><br><span>index 0296693..805e2ff 100644</span><br><span>--- a/lib/python/asterisk/lock_test_condition.py</span><br><span>+++ b/lib/python/asterisk/lock_test_condition.py</span><br><span>@@ -1,4 +1,3 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python</span><br><span> """Held locks test condition</span><br><span> </span><br><span> Copyright (C) 2011-2012, Digium, Inc.</span><br><span>@@ -10,10 +9,9 @@</span><br><span> </span><br><span> import logging</span><br><span> import logging.config</span><br><span style="color: hsl(0, 100%, 40%);">-import unittest</span><br><span> </span><br><span> from twisted.internet import defer</span><br><span style="color: hsl(0, 100%, 40%);">-from test_conditions import TestCondition</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_conditions import TestCondition</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>@@ -199,7 +197,7 @@</span><br><span>     def evaluate(self, related_test_condition=None):</span><br><span>         """Evaluate the condition"""</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        def __lock_info_obtained(finished_deferred):</span><br><span style="color: hsl(120, 100%, 40%);">+        def __lock_info_obtained(lst, finished_deferred):</span><br><span>             """Callback when lock information has been obtained"""</span><br><span>             if (len(self.locks) > 0):</span><br><span>                 #Sometimes, a lock will be held at the end of a test run</span><br><span>@@ -229,314 +227,3 @@</span><br><span>                                          finished_deferred)</span><br><span>         return finished_deferred</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class AstMockObjectPassed(object):</span><br><span style="color: hsl(0, 100%, 40%);">-    """A lock output that passed"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Constructor"""</span><br><span style="color: hsl(0, 100%, 40%);">-        self.host = "127.0.0.2"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def cli_exec(self, command, sync):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Fake out a CLI command execution"""</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines = "=======================================================================\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== Currently Held Locks ==============================================\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=======================================================================\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "===\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== <pending> <lock#> (<file>): <lock type> <line num> <function> <lock name> <lock addr> (times locked)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "===\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "===\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=======================================================================\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        return lock_lines</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class AstMockObjectFailure(object):</span><br><span style="color: hsl(0, 100%, 40%);">-    """A lock object that failed"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Constructor"""</span><br><span style="color: hsl(0, 100%, 40%);">-        self.host = "127.0.0.1"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def cli_exec(self, command, sync):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Fake out a CLI command execution"""</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines = "=======================================================================\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== Currently Held Locks ==============================================\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=======================================================================\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "===\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== <pending> <lock#> (<file>): <lock type> <line num> <function> <lock name> <lock addr> (times locked)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "===\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== Thread ID: 0x402c6940 (do_monitor           started at [25114] chan_sip.c restart_monitor())\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== ---> Lock #0 (chan_sip.c): MUTEX 24629 handle_request_do &netlock 0x2aaabe671a40 (1)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_bt_get_addresses+0x1a) [0x4e9679]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ast_pthread_mutex_lock+0xf6) [0x4e22d9]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/lib/asterisk/modules/chan_sip.so [0x2aaabe423ee8]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== ---> Lock #1 (chan_sip.c): MUTEX 24629 handle_request_do &netlock 0x2aaabe671a40 (1)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== --- ---> Locked Here: channel.c line 4304 (ast_indicate_data)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== -------------------------------------------------------------------\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "===\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== Thread ID: 0x449ec940 (netconsole           started at [ 1351] asterisk.c listener())\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== ---> Waiting for Lock #0 (astobj2.c): MUTEX 842 internal_ao2_iterator_next a->c 0x2aaaac491f50 (1)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_bt_get_addresses+0x1a) [0x4e9679]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ast_pthread_mutex_lock+0xf6) [0x4e22d9]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_lock+0x53) [0x4456fc]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x446cec]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_iterator_next+0x29) [0x447134]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_channel_iterator_next+0x19) [0x46cf7d]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x489e43]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_cli_command_full+0x222) [0x48eec4]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_cli_command_multiple_full+0x92) [0x48f035]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x43d129]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x5661c6]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/lib64/libpthread.so.0 [0x3d1d80673d]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/lib64/libc.so.6(clone+0x6d) [0x3d1ccd44bd]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== --- ---> Locked Here: astobj2.c line 657 (internal_ao2_callback)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== -------------------------------------------------------------------\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "===\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== Thread ID: 0x44a68940 (netconsole           started at [ 1351] asterisk.c listener())\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== ---> Waiting for Lock #0 (astobj2.c): MUTEX 842 internal_ao2_iterator_next a->c 0x2aaaac491f50 (1)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_bt_get_addresses+0x1a) [0x4e9679]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ast_pthread_mutex_lock+0xf6) [0x4e22d9]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_lock+0x53) [0x4456fc]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x446cec]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_iterator_next+0x29) [0x447134]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_channel_iterator_next+0x19) [0x46cf7d]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x489e43]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_cli_command_full+0x222) [0x48eec4]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_cli_command_multiple_full+0x92) [0x48f035]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x43d129]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x5661c6]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/lib64/libpthread.so.0 [0x3d1d80673d]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/lib64/libc.so.6(clone+0x6d) [0x3d1ccd44bd]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== --- ---> Locked Here: astobj2.c line 657 (internal_ao2_callback)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== -------------------------------------------------------------------\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "===\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=======================================================================\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        return lock_lines</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class TestConfig(object):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Fake TestConfig object"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """ Values here don't matter much - we just need to have something """</span><br><span style="color: hsl(0, 100%, 40%);">-        self.class_type_name = "asterisk.LockTestCondition.LockTestCondition"</span><br><span style="color: hsl(0, 100%, 40%);">-        self.pass_expected = True</span><br><span style="color: hsl(0, 100%, 40%);">-        self.type = "Post"</span><br><span style="color: hsl(0, 100%, 40%);">-        self.related_condition = ""</span><br><span style="color: hsl(0, 100%, 40%);">-        self.config = {}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class LockTestConditionUnitTest(unittest.TestCase):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Unit tests for LockTestCondition"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_evaluate_failed(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Test a failed locking condition"""</span><br><span style="color: hsl(0, 100%, 40%);">-        ast = AstMockObjectFailure()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = LockTestCondition(TestConfig())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(ast)</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_evaluate_pass(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Test a passed locking condition"""</span><br><span style="color: hsl(0, 100%, 40%);">-        ast = AstMockObjectPassed()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = LockTestCondition(TestConfig())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(ast)</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Passed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_evaluate_multiple(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Test multiple results"""</span><br><span style="color: hsl(0, 100%, 40%);">-        ast1 = AstMockObjectPassed()</span><br><span style="color: hsl(0, 100%, 40%);">-        ast2 = AstMockObjectFailure()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = LockTestCondition(TestConfig())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(ast1)</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(ast2)</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class LockSequenceUnitTest(unittest.TestCase):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Tests for parsing a lock sequence"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_single_object_no_held_info(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Test a lock sequence with no waiting lock"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines = "=== Thread ID: 0x7f668c142700 (do_monitor           started at [25915] chan_sip.c restart_monitor())\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== ---> Lock #0 (chan_sip.c): MUTEX 25390 handle_request_do &netlock 0x7f6652193900 (1)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "main/logger.c:1302 ast_bt_get_addresses() (0x505e53+1D)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "main/lock.c:193 __ast_pthread_mutex_lock() (0x4fe55c+D9)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "channels/chan_sip.c:25393 handle_request_do()\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "channels/chan_sip.c:25352 sipsock_read()\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "main/io.c:288 ast_io_wait() (0x4f8228+19C)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "channels/chan_sip.c:25882 do_monitor()\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "main/utils.c:1010 dummy_start()\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "libpthread.so.0 <unknown>()\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "libc.so.6 clone() (0x31be0e0bc0+6D)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = LockSequence()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.parse_lock_sequence(lock_lines)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_large_multiple_object(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Test with a waiting lock"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines = "=== Thread ID: 0x449ec940 (netconsole           started at [ 1351] asterisk.c listener())\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== ---> Waiting for Lock #0 (astobj2.c): MUTEX 842 internal_ao2_iterator_next a->c 0x2aaaac491f50 (1)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_bt_get_addresses+0x1a) [0x4e9679]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ast_pthread_mutex_lock+0xf6) [0x4e22d9]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_lock+0x53) [0x4456fc]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x446cec]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_iterator_next+0x29) [0x447134]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_channel_iterator_next+0x19) [0x46cf7d]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x489e43]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_cli_command_full+0x222) [0x48eec4]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_cli_command_multiple_full+0x92) [0x48f035]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x43d129]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x5661c6]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/lib64/libpthread.so.0 [0x3d1d80673d]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/lib64/libc.so.6(clone+0x6d) [0x3d1ccd44bd]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== --- ---> Locked Here: astobj2.c line 657 (internal_ao2_callback)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = LockSequence()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.parse_lock_sequence(lock_lines)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.thread_id, "0x449ec940")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.thread_name, "netconsole")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.thread_line, 1351)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.thread_file, "asterisk.c")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.thread_func, "listener")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(len(obj.locks) == 1)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].locked_file, "astobj2.c")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].locked_line, 657)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].locked_func, "internal_ao2_callback")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_single_object(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Test a lock held somewhere else"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines = "=== Thread ID: 0x402c6940 (do_monitor           started at [25114] chan_sip.c restart_monitor())\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== ---> Lock #0 (chan_sip.c): MUTEX 24629 handle_request_do &netlock 0x2aaabe671a40 (1)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_bt_get_addresses+0x1a) [0x4e9679]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ast_pthread_mutex_lock+0xf6) [0x4e22d9]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/lib/asterisk/modules/chan_sip.so [0x2aaabe423ee8]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== --- ---> Locked Here: channel.c line 4304 (ast_indicate_data)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = LockSequence()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.parse_lock_sequence(lock_lines)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.thread_id, "0x402c6940")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.thread_name, "do_monitor")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.thread_line, 25114)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.thread_file, "chan_sip.c")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.thread_func, "restart_monitor")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(len(obj.locks) == 1)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].locked_file, "channel.c")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].locked_line, 4304)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].locked_func, "ast_indicate_data")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].id, 0)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].type, "MUTEX")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].file, "chan_sip.c")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].line_number, 24629)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].func, "handle_request_do")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].name, "&netlock")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].addr, "0x2aaabe671a40")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].lock_count, 1)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(obj.locks[0].held)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(len(obj.locks[0].backtrace) == 3)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_multiple_objects_no_backtrace(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Test multiple locks with no backtrace"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines = "=== Thread ID: 0x402c6940 (do_monitor           started at [25114] chan_sip.c restart_monitor())\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== ---> Lock #0 (chan_sip.c): MUTEX 24629 handle_request_do &netlock 0x2aaabe671a40 (1)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== ---> Lock #1 (astobj2.c): MUTEX 657 internal_ao2_callback c 0x2aaaac491f50 (1)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== ---> Waiting for Lock #2 (channel.c): MUTEX 1691 ast_channel_cmp_cb chan 0x2aaaacd3a4e0 (1)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "=== --- ---> Locked Here: channel.c line 4304 (ast_indicate_data)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = LockSequence()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.parse_lock_sequence(lock_lines)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.thread_id, "0x402c6940")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.thread_name, "do_monitor")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.thread_line, 25114)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.thread_file, "chan_sip.c")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.thread_func, "restart_monitor")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(len(obj.locks) == 3)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[2].locked_file, "channel.c")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[2].locked_line, 4304)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[2].locked_func, "ast_indicate_data")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].id, 0)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].type, "MUTEX")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].file, "chan_sip.c")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].line_number, 24629)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].func, "handle_request_do")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].name, "&netlock")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].addr, "0x2aaabe671a40")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.locks[0].lock_count, 1)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(obj.locks[0].held)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(len(obj.locks[0].backtrace) == 0)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class LockObjectUnitTest(unittest.TestCase):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Unit tests for LockObject"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_no_backtrace(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Test creating a lock object with no thread backtrace"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_line = "=== ---> Waiting for Lock #0 (sig_ss7.c): MUTEX 636 ss7_linkset &linkset->lock 0x2aaab8a6b588 (1)"</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = LockObject()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.parse_lock_information(lock_line)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.id, 0)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.type, "MUTEX")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.file, "sig_ss7.c")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.line_number, 636)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.func, "ss7_linkset")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.name, "&linkset->lock")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.addr, "0x2aaab8a6b588")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.lock_count, 1)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(obj.held)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(len(obj.backtrace) == 0)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_with_backtrace(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Test creating a lock object with a thread backtrace"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines = "=== ---> Lock #1 (astobj2.c): MUTEX 657 internal_ao2_callback c 0x2aaaac491f50 (1)\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_bt_get_addresses+0x1a) [0x4e9679]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ast_pthread_mutex_lock+0xf6) [0x4e22d9]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_lock+0x53) [0x4456fc]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x4464be]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_callback+0x59) [0x446a4e]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_find+0x2b) [0x446ba7]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x46d3a7]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_channel_get_by_name+0x24) [0x46d3e3]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/lib/asterisk/modules/func_channel.so [0x2aaabfba2468]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_func_write+0x16a) [0x50aacd]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(pbx_builtin_setvar_helper+0x10e) [0x51fff4]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/lib/asterisk/modules/chan_sip.so [0x2aaabe422d09]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/lib/asterisk/modules/chan_sip.so [0x2aaabe4240a0]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/lib/asterisk/modules/chan_sip.so [0x2aaabe423cf1]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_io_wait+0x1ba) [0x4dc2e4]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/lib/asterisk/modules/chan_sip.so [0x2aaabe425722]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x5661c6]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/lib64/libpthread.so.0 [0x3d1d80673d]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        lock_lines += "/lib64/libc.so.6(clone+0x6d) [0x3d1ccd44bd]\n"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = LockObject()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.parse_lock_information(lock_lines)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.id, 1)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.type, "MUTEX")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.file, "astobj2.c")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.line_number, 657)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.func, "internal_ao2_callback")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.name, "c")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.addr, "0x2aaaac491f50")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.lock_count, 1)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(obj.held)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(len(obj.backtrace) == 19)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-def main():</span><br><span style="color: hsl(0, 100%, 40%);">-    """Run the unit tests"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    logging.basicConfig(level=logging.DEBUG)</span><br><span style="color: hsl(0, 100%, 40%);">-    unittest.main()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-if __name__ == "__main__":</span><br><span style="color: hsl(0, 100%, 40%);">-    main()</span><br><span>diff --git a/lib/python/asterisk/matcher.py b/lib/python/asterisk/matcher.py</span><br><span>index 3ab9c24..8ef575e 100644</span><br><span>--- a/lib/python/asterisk/matcher.py</span><br><span>+++ b/lib/python/asterisk/matcher.py</span><br><span>@@ -9,14 +9,18 @@</span><br><span> """</span><br><span> </span><br><span> import logging</span><br><span style="color: hsl(120, 100%, 40%);">+import sys</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from test_suite_utils import all_match</span><br><span style="color: hsl(0, 100%, 40%);">-from pluggable_registry import PLUGGABLE_EVENT_REGISTRY,\</span><br><span style="color: hsl(0, 100%, 40%);">-    PLUGGABLE_ACTION_REGISTRY</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_runner import load_and_parse_module</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_suite_utils import all_match</span><br><span style="color: hsl(120, 100%, 40%);">+from .pluggable_registry import PLUGGABLE_EVENT_REGISTRY</span><br><span> </span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+if sys.version_info[0] == 3:</span><br><span style="color: hsl(120, 100%, 40%);">+    unicode = str</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> </span><br><span> class ConditionError(Exception):</span><br><span>     """Error raised a condition(s) fail"""</span><br><span>@@ -217,7 +221,6 @@</span><br><span> </span><br><span>             matched.append(c)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>         if not matched:</span><br><span>             return False</span><br><span> </span><br><span>@@ -315,13 +318,9 @@</span><br><span> </span><br><span>         self.triggered_callback = triggered_callback</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        module_name, _, obj_type = config['type'].partition('.')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        module = __import__(module_name, fromlist=[obj_type])</span><br><span style="color: hsl(0, 100%, 40%);">-        if not module:</span><br><span style="color: hsl(0, 100%, 40%);">-            raise Exception("Unable to import module '{0}'.".format(module_name))</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = getattr(module, obj_type)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = load_and_parse_module(config['type'])</span><br><span style="color: hsl(120, 100%, 40%);">+        if not obj:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise Exception("Unable to import module '{0}'.".format(config['type']))</span><br><span> </span><br><span>         self.conditions = obj(config, test_object, self.__handle_match)</span><br><span> </span><br><span>diff --git a/lib/python/asterisk/matcher_listener.py b/lib/python/asterisk/matcher_listener.py</span><br><span>index d6b621d..cd2ca5a 100644</span><br><span>--- a/lib/python/asterisk/matcher_listener.py</span><br><span>+++ b/lib/python/asterisk/matcher_listener.py</span><br><span>@@ -13,7 +13,7 @@</span><br><span> from twisted.internet.protocol import DatagramProtocol</span><br><span> from twisted.internet import reactor</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from matcher import PluggableConditions</span><br><span style="color: hsl(120, 100%, 40%);">+from .matcher import PluggableConditions</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>diff --git a/lib/python/asterisk/opensslversion.py b/lib/python/asterisk/opensslversion.py</span><br><span>index a7377ea..3fa5cd5 100644</span><br><span>--- a/lib/python/asterisk/opensslversion.py</span><br><span>+++ b/lib/python/asterisk/opensslversion.py</span><br><span>@@ -9,11 +9,10 @@</span><br><span> """</span><br><span> </span><br><span> import sys</span><br><span style="color: hsl(0, 100%, 40%);">-import unittest</span><br><span> import re</span><br><span> sys.path.append("lib/python")</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-import test_suite_utils</span><br><span style="color: hsl(120, 100%, 40%);">+from . import test_suite_utils</span><br><span> </span><br><span> class OpenSSLVersion:</span><br><span>     """An OpenSSL Version.</span><br><span>diff --git a/lib/python/asterisk/originate.py b/lib/python/asterisk/originate.py</span><br><span>index e6befb5..6c87a57 100644</span><br><span>--- a/lib/python/asterisk/originate.py</span><br><span>+++ b/lib/python/asterisk/originate.py</span><br><span>@@ -16,7 +16,7 @@</span><br><span> import json</span><br><span> import requests</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-import ari</span><br><span style="color: hsl(120, 100%, 40%);">+from . import ari</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>diff --git a/lib/python/asterisk/pcap.py b/lib/python/asterisk/pcap.py</span><br><span>index d39574a..cb5fa5c 100644</span><br><span>--- a/lib/python/asterisk/pcap.py</span><br><span>+++ b/lib/python/asterisk/pcap.py</span><br><span>@@ -739,13 +739,14 @@</span><br><span>         self.callbacks = {}</span><br><span>         self.traces = {}</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def process_packet(self, packet, (host, port)):</span><br><span style="color: hsl(120, 100%, 40%);">+    def process_packet(self, packet, addr):</span><br><span>         """Store a known packet in our traces and call our callbacks</span><br><span> </span><br><span>         Keyword Arguments:</span><br><span>         packet       A raw packet received from ... something.</span><br><span style="color: hsl(0, 100%, 40%);">-        (host, port) Tuple of received host and port</span><br><span style="color: hsl(120, 100%, 40%);">+        addr         Tuple of received host and port</span><br><span>         """</span><br><span style="color: hsl(120, 100%, 40%);">+        (host, port) = addr</span><br><span>         packet = self.packet_factory.interpret_packet(packet)</span><br><span>         if packet is None:</span><br><span>             return</span><br><span>@@ -812,13 +813,14 @@</span><br><span>             self.rules = rules</span><br><span>             self.cb = cb</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        def datagramReceived(self, data, (host, port)):</span><br><span style="color: hsl(120, 100%, 40%);">+        def datagramReceived(self, data, addr):</span><br><span>             """Callback for when a datagram is received</span><br><span> </span><br><span>             Keyword Arguments:</span><br><span>             data         The actual packet</span><br><span style="color: hsl(0, 100%, 40%);">-            (host, port) Tuple of source host and port</span><br><span style="color: hsl(120, 100%, 40%);">+            addr         Tuple of received host and port</span><br><span>             """</span><br><span style="color: hsl(120, 100%, 40%);">+            (host, port) = addr</span><br><span>             LOGGER.debug('Proxy received from {0}:{1}\n{2}'.format(</span><br><span>                 host, port, data))</span><br><span> </span><br><span>diff --git a/lib/python/asterisk/phones.py b/lib/python/asterisk/phones.py</span><br><span>old mode 100755</span><br><span>new mode 100644</span><br><span>index 6a96b38..4987da5</span><br><span>--- a/lib/python/asterisk/phones.py</span><br><span>+++ b/lib/python/asterisk/phones.py</span><br><span>@@ -1,4 +1,3 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python</span><br><span> """Pluggable modules and classes to simulate phones.</span><br><span> </span><br><span> Copyright (C) 2015, Digium, Inc.</span><br><span>@@ -85,7 +84,7 @@</span><br><span> </span><br><span>     def __setup_pjsua_acc_cb(self):</span><br><span>         """Setup PJSUA account callbacks"""</span><br><span style="color: hsl(0, 100%, 40%);">-        for name, phone_obj in self.__pjsua_phones.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+        for name, phone_obj in self.__pjsua_phones.items():</span><br><span>             acc_cb = AccCallback()</span><br><span>             phone_obj.account.set_callback(acc_cb)</span><br><span>             LOGGER.info("%s is ready to receive calls." % name)</span><br><span>@@ -106,7 +105,7 @@</span><br><span>         if name:</span><br><span>             return self.__pjsua_phones.get(name)</span><br><span>         if account:</span><br><span style="color: hsl(0, 100%, 40%);">-            for name, phone_obj in self.__pjsua_phones.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+            for name, phone_obj in self.__pjsua_phones.items():</span><br><span>                 if account is phone_obj.account:</span><br><span>                     return phone_obj</span><br><span> </span><br><span>diff --git a/lib/python/asterisk/pjsua_mod.py b/lib/python/asterisk/pjsua_mod.py</span><br><span>index 63add6a..cdf3d18 100644</span><br><span>--- a/lib/python/asterisk/pjsua_mod.py</span><br><span>+++ b/lib/python/asterisk/pjsua_mod.py</span><br><span>@@ -17,6 +17,7 @@</span><br><span> sys.path.append("lib/python")</span><br><span> </span><br><span> from twisted.internet import reactor</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_runner import load_and_parse_module</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>@@ -147,7 +148,7 @@</span><br><span>             self.lib.set_null_snd_dev()</span><br><span>             self.__create_accounts()</span><br><span>             self.lib.start()</span><br><span style="color: hsl(0, 100%, 40%);">-        except pj.Error, exception:</span><br><span style="color: hsl(120, 100%, 40%);">+        except pj.Error as exception:</span><br><span>             LOGGER.error("Exception: " + str(exception))</span><br><span>             self.lib.destroy()</span><br><span>             self.lib = None</span><br><span>@@ -306,6 +307,5 @@</span><br><span> </span><br><span>     def do_callback(self):</span><br><span>         """Call the configured callback module/method"""</span><br><span style="color: hsl(0, 100%, 40%);">-        callback_module = __import__(self.callback_module)</span><br><span style="color: hsl(0, 100%, 40%);">-        callback_method = getattr(callback_module, self.callback_method)</span><br><span style="color: hsl(120, 100%, 40%);">+        callback_method = load_and_parse_module(self.callback_module + '.' + self.callback_method)</span><br><span>         callback_method(self.test_object, self.pj_accounts)</span><br><span>diff --git a/lib/python/asterisk/pluggable_modules.py b/lib/python/asterisk/pluggable_modules.py</span><br><span>old mode 100755</span><br><span>new mode 100644</span><br><span>index 344f908..dddb036</span><br><span>--- a/lib/python/asterisk/pluggable_modules.py</span><br><span>+++ b/lib/python/asterisk/pluggable_modules.py</span><br><span>@@ -1,4 +1,3 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python</span><br><span> """Generic pluggable modules</span><br><span> </span><br><span> Copyright (C) 2012, Digium, Inc.</span><br><span>@@ -14,15 +13,15 @@</span><br><span> import re</span><br><span> </span><br><span> sys.path.append("lib/python")</span><br><span style="color: hsl(0, 100%, 40%);">-from ami import AMIEventInstance</span><br><span style="color: hsl(120, 100%, 40%);">+from .ami import AMIEventInstance</span><br><span> from twisted.internet import reactor</span><br><span> from starpy import fastagi</span><br><span style="color: hsl(0, 100%, 40%);">-from test_runner import load_and_parse_module</span><br><span style="color: hsl(0, 100%, 40%);">-from pluggable_registry import PLUGGABLE_ACTION_REGISTRY,\</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_runner import load_and_parse_module</span><br><span style="color: hsl(120, 100%, 40%);">+from .pluggable_registry import PLUGGABLE_ACTION_REGISTRY,\</span><br><span>     PLUGGABLE_EVENT_REGISTRY,\</span><br><span>     PluggableRegistry</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-import matcher</span><br><span style="color: hsl(120, 100%, 40%);">+from . import matcher</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>@@ -576,7 +575,7 @@</span><br><span>             return</span><br><span> </span><br><span>         current_trigger = config['trigger']['match']</span><br><span style="color: hsl(0, 100%, 40%);">-        for key, value in current_trigger.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+        for key, value in current_trigger.items():</span><br><span>             if key.lower() not in event:</span><br><span>                 LOGGER.debug("Condition %s not in event, returning", key)</span><br><span>                 return</span><br><span>@@ -701,8 +700,7 @@</span><br><span>         if self.commands:</span><br><span>             return self.execute_command(agi, 0)</span><br><span>         else:</span><br><span style="color: hsl(0, 100%, 40%);">-            callback_module = __import__(self.callback_module)</span><br><span style="color: hsl(0, 100%, 40%);">-            method = getattr(callback_module, self.callback_method)</span><br><span style="color: hsl(120, 100%, 40%);">+            method = load_and_parse_module(self.callback_module + '.' + self.callback_method)</span><br><span>             method(self.test_object, agi)</span><br><span> </span><br><span>     def on_command_failure(self, reason, agi, idx):</span><br><span>@@ -784,7 +782,7 @@</span><br><span> </span><br><span>         def register_modules(config, registry):</span><br><span>             """Register pluggable modules into the registry"""</span><br><span style="color: hsl(0, 100%, 40%);">-            for key, local_class_path in config.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+            for key, local_class_path in config.items():</span><br><span>                 local_class = load_and_parse_module(local_class_path)</span><br><span>                 if not local_class:</span><br><span>                     raise Exception("Unable to load %s for module key %s"</span><br><span>@@ -804,7 +802,7 @@</span><br><span>         for e_a_set in config["mapping"]:</span><br><span>             plug_set = {"events": [], "actions": []}</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-            for plug_name, plug_config in e_a_set.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+            for plug_name, plug_config in e_a_set.items():</span><br><span>                 self.parse_module_config(plug_set, plug_name, plug_config)</span><br><span> </span><br><span>             if 0 == len(plug_set["events"]):</span><br><span>@@ -923,8 +921,7 @@</span><br><span> </span><br><span>     def run(self, triggered_by, source, extra):</span><br><span>         """Call the callback."""</span><br><span style="color: hsl(0, 100%, 40%);">-        module = __import__(self.module)</span><br><span style="color: hsl(0, 100%, 40%);">-        method = getattr(module, self.method)</span><br><span style="color: hsl(120, 100%, 40%);">+        method = load_and_parse_module(self.module + '.' + self.method)</span><br><span>         self.test_object.set_passed(method(self.test_object, triggered_by,</span><br><span>                                            source, extra))</span><br><span> PLUGGABLE_ACTION_REGISTRY.register("callback", CallbackActionModule)</span><br><span>@@ -960,12 +957,12 @@</span><br><span>     def __init__(self, test_object, config):</span><br><span>         """Setup the test start observer"""</span><br><span>         self.test_object = test_object</span><br><span style="color: hsl(0, 100%, 40%);">-        self.module = __import__("phones")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.module = "phones"</span><br><span>         self.method = config["action"]</span><br><span>         self.config = config</span><br><span> </span><br><span>     def run(self, triggered_by, source, extra):</span><br><span>         """Instruct phone to perform action"""</span><br><span style="color: hsl(0, 100%, 40%);">-        method = getattr(self.module, self.method)</span><br><span style="color: hsl(120, 100%, 40%);">+        method = load_and_parse_module(self.module + "." + self.method)</span><br><span>         method(self.test_object, triggered_by, source, extra, self.config)</span><br><span> PLUGGABLE_ACTION_REGISTRY.register("pjsua_phone", PjsuaPhoneActionModule)</span><br><span>diff --git a/lib/python/asterisk/pluggable_registry.py b/lib/python/asterisk/pluggable_registry.py</span><br><span>old mode 100755</span><br><span>new mode 100644</span><br><span>index 8169795..9a50a26</span><br><span>--- a/lib/python/asterisk/pluggable_registry.py</span><br><span>+++ b/lib/python/asterisk/pluggable_registry.py</span><br><span>@@ -1,4 +1,3 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python</span><br><span> """Pluggable module registries</span><br><span> </span><br><span> Copyright (C) 2014, Digium, Inc.</span><br><span>diff --git a/lib/python/asterisk/realtime_converter.py b/lib/python/asterisk/realtime_converter.py</span><br><span>index 3f50b5f..95ae579 100644</span><br><span>--- a/lib/python/asterisk/realtime_converter.py</span><br><span>+++ b/lib/python/asterisk/realtime_converter.py</span><br><span>@@ -9,7 +9,7 @@</span><br><span> import os</span><br><span> from sqlalchemy import create_engine, MetaData, Table</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-import astconfigparser</span><br><span style="color: hsl(120, 100%, 40%);">+from . import astconfigparser</span><br><span> import logging</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span>@@ -83,8 +83,8 @@</span><br><span>         self.filename = filename</span><br><span>         self.sections = sections</span><br><span>         # All affected database tables in list form. Used for convenience</span><br><span style="color: hsl(0, 100%, 40%);">-        self.tables = [table for section in sections.itervalues() for table in</span><br><span style="color: hsl(0, 100%, 40%);">-                       section.itervalues()]</span><br><span style="color: hsl(120, 100%, 40%);">+        self.tables = [table for section in sections.values() for table in</span><br><span style="color: hsl(120, 100%, 40%);">+                       section.values()]</span><br><span>         self.sorcery = None</span><br><span>         self.extconfig = None</span><br><span> </span><br><span>@@ -111,9 +111,9 @@</span><br><span>         config_dir: The directory where Asterisk configuration can be found</span><br><span>         """</span><br><span>         with open(self.sorcery.file, 'a') as sorcery:</span><br><span style="color: hsl(0, 100%, 40%);">-            for section, items in self.sections.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+            for section, items in self.sections.items():</span><br><span>                 sorcery.write('[{0}]\n'.format(section))</span><br><span style="color: hsl(0, 100%, 40%);">-                for obj, table in items.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+                for obj, table in items.items():</span><br><span>                     sorcery.write('{0} = realtime,{1}\n'.format(obj, table))</span><br><span> </span><br><span>     def write_extconfig_conf(self):</span><br><span>@@ -141,7 +141,7 @@</span><br><span>         """</span><br><span>         conf = astconfigparser.MultiOrderedConfigParser()</span><br><span>         conf.read(os.path.join(config_dir, self.filename))</span><br><span style="color: hsl(0, 100%, 40%);">-        for title, sections in conf.sections().iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+        for title, sections in conf.sections().items():</span><br><span>             LOGGER.info("Inspecting objects with title {0}".format(title))</span><br><span>             for section in sections:</span><br><span>                 obj_type = section.get('type')[0]</span><br><span>@@ -165,7 +165,7 @@</span><br><span>         Keyword Arguments:</span><br><span>         obj_type: The object type to find the section for</span><br><span>         """</span><br><span style="color: hsl(0, 100%, 40%);">-        for section, contents in self.sections.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+        for section, contents in self.sections.items():</span><br><span>             if obj_type in contents:</span><br><span>                 return section</span><br><span> </span><br><span>diff --git a/lib/python/asterisk/realtime_odbc_module.py b/lib/python/asterisk/realtime_odbc_module.py</span><br><span>index 3653553..bbd8a8e 100644</span><br><span>--- a/lib/python/asterisk/realtime_odbc_module.py</span><br><span>+++ b/lib/python/asterisk/realtime_odbc_module.py</span><br><span>@@ -53,7 +53,7 @@</span><br><span>         self.res_odbc = {}</span><br><span> </span><br><span>         # generate configuration for each dsn</span><br><span style="color: hsl(0, 100%, 40%);">-        for dsn, config in module_config.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+        for dsn, config in module_config.items():</span><br><span>             self._configure(dsn, config)</span><br><span> </span><br><span>         # set the odbc and conf files</span><br><span>@@ -109,7 +109,7 @@</span><br><span>         with open(filepath, 'w') as filehandle:</span><br><span>             for section in contents:</span><br><span>                 filehandle.write('[' + section + ']\n')</span><br><span style="color: hsl(0, 100%, 40%);">-                for name, value in contents[section].iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+                for name, value in contents[section].items():</span><br><span>                     filehandle.write(name + '=' + value + '\n')</span><br><span> </span><br><span>     def _read_ini_file(self, filepath):</span><br><span>diff --git a/lib/python/asterisk/realtime_test_module.py b/lib/python/asterisk/realtime_test_module.py</span><br><span>index fd38aa9..da97422 100644</span><br><span>--- a/lib/python/asterisk/realtime_test_module.py</span><br><span>+++ b/lib/python/asterisk/realtime_test_module.py</span><br><span>@@ -91,7 +91,7 @@</span><br><span> </span><br><span>         return [row for row in table</span><br><span>                 if all(key in row and re.match(value, row[key])</span><br><span style="color: hsl(0, 100%, 40%);">-                       for key, value in where.iteritems())]</span><br><span style="color: hsl(120, 100%, 40%);">+                       for key, value in where.items())]</span><br><span> </span><br><span>     def retrieve_rows(self, table_name, where):</span><br><span>         """Retrieve multiple rows from a table.</span><br><span>@@ -239,7 +239,7 @@</span><br><span>         since we could use a dict comprehension.</span><br><span>         """</span><br><span>         filtered_args = {}</span><br><span style="color: hsl(0, 100%, 40%);">-        for key, values in args.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+        for key, values in args.items():</span><br><span>             if " LIKE" in key:</span><br><span>                 # Strip away " LIKE" and % from values</span><br><span>                 filtered_args[key[:-5]] = [val.replace('%', '.*')</span><br><span>@@ -250,7 +250,7 @@</span><br><span>         LOGGER.debug('filtered args is %s' % filtered_args)</span><br><span> </span><br><span>         return dict((key, values[0] if values else '.*') for key, values in</span><br><span style="color: hsl(0, 100%, 40%);">-                    filtered_args.iteritems())</span><br><span style="color: hsl(120, 100%, 40%);">+                    filtered_args.items())</span><br><span> </span><br><span>     def encode_row(self, row):</span><br><span>         """Encode a retrieved row for an HTTP response.</span><br><span>@@ -261,7 +261,7 @@</span><br><span>         Example output: 'foo=cat&bar=dog&baz=donkey'</span><br><span>         """</span><br><span>         string = '&'.join(['{0}={1}'.format(cgi.escape(key), cgi.escape(val))</span><br><span style="color: hsl(0, 100%, 40%);">-                           for key, val in row.iteritems()])</span><br><span style="color: hsl(120, 100%, 40%);">+                           for key, val in row.items()])</span><br><span>         LOGGER.debug("Returning response %s" % string)</span><br><span>         return string</span><br><span> </span><br><span>@@ -481,7 +481,7 @@</span><br><span>         if not data:</span><br><span>             return</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        for table_name, rows in data.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+        for table_name, rows in data.items():</span><br><span>             self.rt_data.add_rows(table_name, rows)</span><br><span> </span><br><span>     def setup_http(self):</span><br><span>diff --git a/lib/python/asterisk/self_test/harness_shared.py b/lib/python/asterisk/self_test/harness_shared.py</span><br><span>new file mode 100644</span><br><span>index 0000000..91449ac</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/self_test/harness_shared.py</span><br><span>@@ -0,0 +1,54 @@</span><br><span style="color: hsl(120, 100%, 40%);">+"""Unit test harness</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This module provides the entry-point for tests</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Copyright (C) 2018, CFWare, LLC.</span><br><span style="color: hsl(120, 100%, 40%);">+Corey Farrell <git@cfware.com></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+the GNU General Public License Version 2.</span><br><span style="color: hsl(120, 100%, 40%);">+"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+import logging</span><br><span style="color: hsl(120, 100%, 40%);">+import os</span><br><span style="color: hsl(120, 100%, 40%);">+import sys</span><br><span style="color: hsl(120, 100%, 40%);">+from twisted.internet import defer</span><br><span style="color: hsl(120, 100%, 40%);">+import unittest</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# Add directory where the modules to test can be found</span><br><span style="color: hsl(120, 100%, 40%);">+sys.path.append('lib/python')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def ReadTestFile(filename, basepath="lib/python/asterisk/self_test"):</span><br><span style="color: hsl(120, 100%, 40%);">+    fd = open(os.path.join(basepath, filename), "r")</span><br><span style="color: hsl(120, 100%, 40%);">+    output = fd.read()</span><br><span style="color: hsl(120, 100%, 40%);">+    fd.close()</span><br><span style="color: hsl(120, 100%, 40%);">+    return output</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AstMockOutput(object):</span><br><span style="color: hsl(120, 100%, 40%);">+    """mock cli output base class"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, host="127.0.0.1"):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Constructor"""</span><br><span style="color: hsl(120, 100%, 40%);">+        self.host = host</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def MockDeferFile(self, filename):</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.MockDefer(ReadTestFile(filename))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def MockDefer(self, output):</span><br><span style="color: hsl(120, 100%, 40%);">+        """use real defer to mock deferred output"""</span><br><span style="color: hsl(120, 100%, 40%);">+        self.output = output</span><br><span style="color: hsl(120, 100%, 40%);">+        deferred = defer.Deferred()</span><br><span style="color: hsl(120, 100%, 40%);">+        deferred.callback(self)</span><br><span style="color: hsl(120, 100%, 40%);">+        return deferred</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def main():</span><br><span style="color: hsl(120, 100%, 40%);">+    """Run the unit tests"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    logging.basicConfig()</span><br><span style="color: hsl(120, 100%, 40%);">+    unittest.main()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+__all__ = ["main", "AstMockOutput", "ReadTestFile"]</span><br><span>diff --git a/lib/python/asterisk/self_test/locks-backtrace.txt b/lib/python/asterisk/self_test/locks-backtrace.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..0de89a4</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/self_test/locks-backtrace.txt</span><br><span>@@ -0,0 +1,20 @@</span><br><span style="color: hsl(120, 100%, 40%);">+=== ---> Lock #1 (astobj2.c): MUTEX 657 internal_ao2_callback c 0x2aaaac491f50 (1)</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_bt_get_addresses+0x1a) [0x4e9679]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ast_pthread_mutex_lock+0xf6) [0x4e22d9]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_lock+0x53) [0x4456fc]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x4464be]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_callback+0x59) [0x446a4e]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_find+0x2b) [0x446ba7]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x46d3a7]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_channel_get_by_name+0x24) [0x46d3e3]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/lib/asterisk/modules/func_channel.so [0x2aaabfba2468]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_func_write+0x16a) [0x50aacd]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(pbx_builtin_setvar_helper+0x10e) [0x51fff4]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/lib/asterisk/modules/chan_sip.so [0x2aaabe422d09]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/lib/asterisk/modules/chan_sip.so [0x2aaabe4240a0]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/lib/asterisk/modules/chan_sip.so [0x2aaabe423cf1]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_io_wait+0x1ba) [0x4dc2e4]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/lib/asterisk/modules/chan_sip.so [0x2aaabe425722]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x5661c6]</span><br><span style="color: hsl(120, 100%, 40%);">+/lib64/libpthread.so.0 [0x3d1d80673d]</span><br><span style="color: hsl(120, 100%, 40%);">+/lib64/libc.so.6(clone+0x6d) [0x3d1ccd44bd]</span><br><span>diff --git a/lib/python/asterisk/self_test/locks-fail.txt b/lib/python/asterisk/self_test/locks-fail.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..fb50ca8</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/self_test/locks-fail.txt</span><br><span>@@ -0,0 +1,52 @@</span><br><span style="color: hsl(120, 100%, 40%);">+=======================================================================</span><br><span style="color: hsl(120, 100%, 40%);">+=== Currently Held Locks ==============================================</span><br><span style="color: hsl(120, 100%, 40%);">+=======================================================================</span><br><span style="color: hsl(120, 100%, 40%);">+===</span><br><span style="color: hsl(120, 100%, 40%);">+=== <pending> <lock#> (<file>): <lock type> <line num> <function> <lock name> <lock addr> (times locked)</span><br><span style="color: hsl(120, 100%, 40%);">+===</span><br><span style="color: hsl(120, 100%, 40%);">+=== Thread ID: 0x402c6940 (do_monitor           started at [25114] chan_sip.c restart_monitor())</span><br><span style="color: hsl(120, 100%, 40%);">+=== ---> Lock #0 (chan_sip.c): MUTEX 24629 handle_request_do &netlock 0x2aaabe671a40 (1)</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_bt_get_addresses+0x1a) [0x4e9679]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ast_pthread_mutex_lock+0xf6) [0x4e22d9]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/lib/asterisk/modules/chan_sip.so [0x2aaabe423ee8]</span><br><span style="color: hsl(120, 100%, 40%);">+=== ---> Lock #1 (chan_sip.c): MUTEX 24629 handle_request_do &netlock 0x2aaabe671a40 (1)</span><br><span style="color: hsl(120, 100%, 40%);">+=== --- ---> Locked Here: channel.c line 4304 (ast_indicate_data)</span><br><span style="color: hsl(120, 100%, 40%);">+=== -------------------------------------------------------------------</span><br><span style="color: hsl(120, 100%, 40%);">+===</span><br><span style="color: hsl(120, 100%, 40%);">+=== Thread ID: 0x449ec940 (netconsole           started at [ 1351] asterisk.c listener())</span><br><span style="color: hsl(120, 100%, 40%);">+=== ---> Waiting for Lock #0 (astobj2.c): MUTEX 842 internal_ao2_iterator_next a->c 0x2aaaac491f50 (1)</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_bt_get_addresses+0x1a) [0x4e9679]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ast_pthread_mutex_lock+0xf6) [0x4e22d9]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_lock+0x53) [0x4456fc]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x446cec]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_iterator_next+0x29) [0x447134]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_channel_iterator_next+0x19) [0x46cf7d]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x489e43]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_cli_command_full+0x222) [0x48eec4]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_cli_command_multiple_full+0x92) [0x48f035]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x43d129]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x5661c6]</span><br><span style="color: hsl(120, 100%, 40%);">+/lib64/libpthread.so.0 [0x3d1d80673d]</span><br><span style="color: hsl(120, 100%, 40%);">+/lib64/libc.so.6(clone+0x6d) [0x3d1ccd44bd]</span><br><span style="color: hsl(120, 100%, 40%);">+=== --- ---> Locked Here: astobj2.c line 657 (internal_ao2_callback)</span><br><span style="color: hsl(120, 100%, 40%);">+=== -------------------------------------------------------------------</span><br><span style="color: hsl(120, 100%, 40%);">+===</span><br><span style="color: hsl(120, 100%, 40%);">+=== Thread ID: 0x44a68940 (netconsole           started at [ 1351] asterisk.c listener())</span><br><span style="color: hsl(120, 100%, 40%);">+=== ---> Waiting for Lock #0 (astobj2.c): MUTEX 842 internal_ao2_iterator_next a->c 0x2aaaac491f50 (1)</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_bt_get_addresses+0x1a) [0x4e9679]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ast_pthread_mutex_lock+0xf6) [0x4e22d9]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_lock+0x53) [0x4456fc]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x446cec]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_iterator_next+0x29) [0x447134]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_channel_iterator_next+0x19) [0x46cf7d]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x489e43]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_cli_command_full+0x222) [0x48eec4]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_cli_command_multiple_full+0x92) [0x48f035]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x43d129]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x5661c6]</span><br><span style="color: hsl(120, 100%, 40%);">+/lib64/libpthread.so.0 [0x3d1d80673d]</span><br><span style="color: hsl(120, 100%, 40%);">+/lib64/libc.so.6(clone+0x6d) [0x3d1ccd44bd]</span><br><span style="color: hsl(120, 100%, 40%);">+=== --- ---> Locked Here: astobj2.c line 657 (internal_ao2_callback)</span><br><span style="color: hsl(120, 100%, 40%);">+=== -------------------------------------------------------------------</span><br><span style="color: hsl(120, 100%, 40%);">+===</span><br><span style="color: hsl(120, 100%, 40%);">+=======================================================================</span><br><span>diff --git a/lib/python/asterisk/self_test/locks-large-multiple-object.txt b/lib/python/asterisk/self_test/locks-large-multiple-object.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..ea657b6</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/self_test/locks-large-multiple-object.txt</span><br><span>@@ -0,0 +1,16 @@</span><br><span style="color: hsl(120, 100%, 40%);">+=== Thread ID: 0x449ec940 (netconsole           started at [ 1351] asterisk.c listener())</span><br><span style="color: hsl(120, 100%, 40%);">+=== ---> Waiting for Lock #0 (astobj2.c): MUTEX 842 internal_ao2_iterator_next a->c 0x2aaaac491f50 (1)</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_bt_get_addresses+0x1a) [0x4e9679]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ast_pthread_mutex_lock+0xf6) [0x4e22d9]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_lock+0x53) [0x4456fc]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x446cec]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ao2_iterator_next+0x29) [0x447134]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_channel_iterator_next+0x19) [0x46cf7d]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x489e43]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_cli_command_full+0x222) [0x48eec4]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_cli_command_multiple_full+0x92) [0x48f035]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x43d129]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk [0x5661c6]</span><br><span style="color: hsl(120, 100%, 40%);">+/lib64/libpthread.so.0 [0x3d1d80673d]</span><br><span style="color: hsl(120, 100%, 40%);">+/lib64/libc.so.6(clone+0x6d) [0x3d1ccd44bd]</span><br><span style="color: hsl(120, 100%, 40%);">+=== --- ---> Locked Here: astobj2.c line 657 (internal_ao2_callback)</span><br><span>diff --git a/lib/python/asterisk/self_test/locks-multiple-objects-no-backtrace.txt b/lib/python/asterisk/self_test/locks-multiple-objects-no-backtrace.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..3ed0ca5</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/self_test/locks-multiple-objects-no-backtrace.txt</span><br><span>@@ -0,0 +1,5 @@</span><br><span style="color: hsl(120, 100%, 40%);">+=== Thread ID: 0x402c6940 (do_monitor           started at [25114] chan_sip.c restart_monitor())</span><br><span style="color: hsl(120, 100%, 40%);">+=== ---> Lock #0 (chan_sip.c): MUTEX 24629 handle_request_do &netlock 0x2aaabe671a40 (1)</span><br><span style="color: hsl(120, 100%, 40%);">+=== ---> Lock #1 (astobj2.c): MUTEX 657 internal_ao2_callback c 0x2aaaac491f50 (1)</span><br><span style="color: hsl(120, 100%, 40%);">+=== ---> Waiting for Lock #2 (channel.c): MUTEX 1691 ast_channel_cmp_cb chan 0x2aaaacd3a4e0 (1)</span><br><span style="color: hsl(120, 100%, 40%);">+=== --- ---> Locked Here: channel.c line 4304 (ast_indicate_data)</span><br><span>diff --git a/lib/python/asterisk/self_test/locks-pass.txt b/lib/python/asterisk/self_test/locks-pass.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..bbb3b92</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/self_test/locks-pass.txt</span><br><span>@@ -0,0 +1,8 @@</span><br><span style="color: hsl(120, 100%, 40%);">+=======================================================================</span><br><span style="color: hsl(120, 100%, 40%);">+=== Currently Held Locks ==============================================</span><br><span style="color: hsl(120, 100%, 40%);">+=======================================================================</span><br><span style="color: hsl(120, 100%, 40%);">+===</span><br><span style="color: hsl(120, 100%, 40%);">+=== <pending> <lock#> (<file>): <lock type> <line num> <function> <lock name> <lock addr> (times locked)</span><br><span style="color: hsl(120, 100%, 40%);">+===</span><br><span style="color: hsl(120, 100%, 40%);">+===</span><br><span style="color: hsl(120, 100%, 40%);">+=======================================================================</span><br><span>diff --git a/lib/python/asterisk/self_test/locks-single-object-no-held-info.txt b/lib/python/asterisk/self_test/locks-single-object-no-held-info.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..e1bf47a</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/self_test/locks-single-object-no-held-info.txt</span><br><span>@@ -0,0 +1,11 @@</span><br><span style="color: hsl(120, 100%, 40%);">+=== Thread ID: 0x7f668c142700 (do_monitor           started at [25915] chan_sip.c restart_monitor())</span><br><span style="color: hsl(120, 100%, 40%);">+=== ---> Lock #0 (chan_sip.c): MUTEX 25390 handle_request_do &netlock 0x7f6652193900 (1)</span><br><span style="color: hsl(120, 100%, 40%);">+main/logger.c:1302 ast_bt_get_addresses() (0x505e53+1D)</span><br><span style="color: hsl(120, 100%, 40%);">+main/lock.c:193 __ast_pthread_mutex_lock() (0x4fe55c+D9)</span><br><span style="color: hsl(120, 100%, 40%);">+channels/chan_sip.c:25393 handle_request_do()</span><br><span style="color: hsl(120, 100%, 40%);">+channels/chan_sip.c:25352 sipsock_read()</span><br><span style="color: hsl(120, 100%, 40%);">+main/io.c:288 ast_io_wait() (0x4f8228+19C)</span><br><span style="color: hsl(120, 100%, 40%);">+channels/chan_sip.c:25882 do_monitor()</span><br><span style="color: hsl(120, 100%, 40%);">+main/utils.c:1010 dummy_start()</span><br><span style="color: hsl(120, 100%, 40%);">+libpthread.so.0 <unknown>()</span><br><span style="color: hsl(120, 100%, 40%);">+libc.so.6 clone() (0x31be0e0bc0+6D)</span><br><span>diff --git a/lib/python/asterisk/self_test/locks-single-object.txt b/lib/python/asterisk/self_test/locks-single-object.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..db356ed</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/self_test/locks-single-object.txt</span><br><span>@@ -0,0 +1,6 @@</span><br><span style="color: hsl(120, 100%, 40%);">+=== Thread ID: 0x402c6940 (do_monitor           started at [25114] chan_sip.c restart_monitor())</span><br><span style="color: hsl(120, 100%, 40%);">+=== ---> Lock #0 (chan_sip.c): MUTEX 24629 handle_request_do &netlock 0x2aaabe671a40 (1)</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(ast_bt_get_addresses+0x1a) [0x4e9679]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/sbin/asterisk(__ast_pthread_mutex_lock+0xf6) [0x4e22d9]</span><br><span style="color: hsl(120, 100%, 40%);">+/usr/local/asterisk-1.8.6.0/lib/asterisk/modules/chan_sip.so [0x2aaabe423ee8]</span><br><span style="color: hsl(120, 100%, 40%);">+=== --- ---> Locked Here: channel.c line 4304 (ast_indicate_data)</span><br><span>diff --git a/lib/python/asterisk/self_test/test_buildoptions.py b/lib/python/asterisk/self_test/test_buildoptions.py</span><br><span>new file mode 100755</span><br><span>index 0000000..1f4ca77</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/self_test/test_buildoptions.py</span><br><span>@@ -0,0 +1,26 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#!/usr/bin/env python</span><br><span style="color: hsl(120, 100%, 40%);">+"""Asterisk Build Options Handling Unit Test</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Copyright (C) 2011-2012, Digium, Inc.</span><br><span style="color: hsl(120, 100%, 40%);">+Matt Jordan <mjordan@digium.com></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+the GNU General Public License Version 2.</span><br><span style="color: hsl(120, 100%, 40%);">+"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from harness_shared import main</span><br><span style="color: hsl(120, 100%, 40%);">+import unittest</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.buildoptions import AsteriskBuildOptions</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AsteriskBuildOptionsTests(unittest.TestCase):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Unit tests for AsteriskBuildOptions"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_1(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Test the defaults paths"""</span><br><span style="color: hsl(120, 100%, 40%);">+        build_options = AsteriskBuildOptions()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(build_options)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+if __name__ == "__main__":</span><br><span style="color: hsl(120, 100%, 40%);">+    main()</span><br><span>diff --git a/lib/python/asterisk/self_test/test_cdr.py b/lib/python/asterisk/self_test/test_cdr.py</span><br><span>new file mode 100755</span><br><span>index 0000000..251530b</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/self_test/test_cdr.py</span><br><span>@@ -0,0 +1,44 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#!/usr/bin/env python</span><br><span style="color: hsl(120, 100%, 40%);">+"""Asterisk call detail record unit tests</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This module implements an Asterisk CDR parser.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Copyright (C) 2010, Digium, Inc.</span><br><span style="color: hsl(120, 100%, 40%);">+Terry Wilson<twilson@digium.com></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+the GNU General Public License Version 2.</span><br><span style="color: hsl(120, 100%, 40%);">+"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from harness_shared import main</span><br><span style="color: hsl(120, 100%, 40%);">+import unittest</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.cdr import AsteriskCSVCDR, AsteriskCSVCDRLine</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AsteriskCSVCDRTests(unittest.TestCase):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Unit tests for AsteriskCSVCDR"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cdr(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Test the self_test/Master.csv record"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        cdr = AsteriskCSVCDR("lib/python/asterisk/self_test/Master.csv")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(len(cdr), 2)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(</span><br><span style="color: hsl(120, 100%, 40%);">+            AsteriskCSVCDRLine(duration=7, lastapp="hangup").match(</span><br><span style="color: hsl(120, 100%, 40%);">+                cdr[0],</span><br><span style="color: hsl(120, 100%, 40%);">+                exact=(True, True)))</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(cdr[0].match(</span><br><span style="color: hsl(120, 100%, 40%);">+            AsteriskCSVCDRLine(duration=7, lastapp="hangup"),</span><br><span style="color: hsl(120, 100%, 40%);">+            exact=(True, True)))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(cdr[1].match(cdr[0], silent=True, exact=(True, True)))</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(cdr[0].match(cdr[1], silent=True, exact=(True, True)))</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(cdr[0].billsec, "7")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(cdr.match(cdr))</span><br><span style="color: hsl(120, 100%, 40%);">+        cdr2 = AsteriskCSVCDR("lib/python/asterisk/self_test/Master2.csv")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(cdr.match(cdr2))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+if __name__ == '__main__':</span><br><span style="color: hsl(120, 100%, 40%);">+    main()</span><br><span>diff --git a/lib/python/asterisk/self_test/test_cel.py b/lib/python/asterisk/self_test/test_cel.py</span><br><span>new file mode 100755</span><br><span>index 0000000..3ea8aa9</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/self_test/test_cel.py</span><br><span>@@ -0,0 +1,47 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#!/usr/bin/env python</span><br><span style="color: hsl(120, 100%, 40%);">+"""Asterisk call detail record unit tests</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This module implements an Asterisk CDR parser.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Copyright (C) 2010, Digium, Inc.</span><br><span style="color: hsl(120, 100%, 40%);">+Terry Wilson<twilson@digium.com></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+the GNU General Public License Version 2.</span><br><span style="color: hsl(120, 100%, 40%);">+"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from harness_shared import main</span><br><span style="color: hsl(120, 100%, 40%);">+import unittest</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.cel import AsteriskCSVCEL, AsteriskCSVCELLine</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AsteriskCSVCELTests(unittest.TestCase):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Unit tests for AsteriskCSVCEL"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cel(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Test CEL using self_test/CELMaster1.csv"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        cel = AsteriskCSVCEL("lib/python/asterisk/self_test/CELMaster1.csv")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(len(cel), 16)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(AsteriskCSVCELLine(</span><br><span style="color: hsl(120, 100%, 40%);">+            eventtype="LINKEDID_END",</span><br><span style="color: hsl(120, 100%, 40%);">+            channel="TinCan/string").match(cel[-1],</span><br><span style="color: hsl(120, 100%, 40%);">+                                           silent=True,</span><br><span style="color: hsl(120, 100%, 40%);">+                                           exact=(True, True)))</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(cel[-1].match(</span><br><span style="color: hsl(120, 100%, 40%);">+            AsteriskCSVCELLine(eventtype="LINKEDID_END",</span><br><span style="color: hsl(120, 100%, 40%);">+                               channel="TinCan/string"),</span><br><span style="color: hsl(120, 100%, 40%);">+            silent=True,</span><br><span style="color: hsl(120, 100%, 40%);">+            exact=(True, True)))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(cel[1].match(cel[0], silent=True, exact=(True, True)))</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(cel[0].match(cel[1], silent=True, exact=(True, True)))</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(cel[-1].channel, "TinCan/string")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(cel.match(cel))</span><br><span style="color: hsl(120, 100%, 40%);">+        cel2 = AsteriskCSVCEL("lib/python/asterisk/self_test/CELMaster2.csv")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(cel.match(cel2))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+if __name__ == '__main__':</span><br><span style="color: hsl(120, 100%, 40%);">+    main()</span><br><span>diff --git a/lib/python/asterisk/self_test/test_channel_test_condition.py b/lib/python/asterisk/self_test/test_channel_test_condition.py</span><br><span>new file mode 100755</span><br><span>index 0000000..e32d883</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/self_test/test_channel_test_condition.py</span><br><span>@@ -0,0 +1,144 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#!/usr/bin/env python</span><br><span style="color: hsl(120, 100%, 40%);">+"""Tests for test condition for channels</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Copyright (C) 2011-2012, Digium, Inc.</span><br><span style="color: hsl(120, 100%, 40%);">+Matt Jordan <mjordan@digium.com></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+the GNU General Public License Version 2.</span><br><span style="color: hsl(120, 100%, 40%);">+"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from harness_shared import AstMockOutput, main</span><br><span style="color: hsl(120, 100%, 40%);">+import unittest</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.channel_test_condition import ChannelTestCondition</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AstMockObjectInactive(AstMockOutput):</span><br><span style="color: hsl(120, 100%, 40%);">+    """mock cli output showing no active channels"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def cli_exec(self, command):</span><br><span style="color: hsl(120, 100%, 40%);">+        """presume command is core show channels and generate output"""</span><br><span style="color: hsl(120, 100%, 40%);">+        output = ""</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "Channel              Location             State   Application(Data)\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "0 active channels\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "0 active calls\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "2 calls processed\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "Asterisk ending (0).\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.MockDefer(output)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AstMockObjectSingle(AstMockOutput):</span><br><span style="color: hsl(120, 100%, 40%);">+    """mock cli output showing single active channel"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def cli_exec(self, command):</span><br><span style="color: hsl(120, 100%, 40%);">+        """presume command is core show channels and generate output"""</span><br><span style="color: hsl(120, 100%, 40%);">+        output = ""</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "Channel              Location             State   Application(Data)\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "Local/123@default-00 (None)               Down    ()\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "1 active channels\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "0 active calls\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "2 calls processed\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "Asterisk ending (0).\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.MockDefer(output)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AstMockObjectMultiple(AstMockOutput):</span><br><span style="color: hsl(120, 100%, 40%);">+    """mock cli output showing multiple active channels"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def cli_exec(self, command):</span><br><span style="color: hsl(120, 100%, 40%);">+        """presume command is core show channels and generate output"""</span><br><span style="color: hsl(120, 100%, 40%);">+        output = ""</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "Channel              Location             State   Application(Data)\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "PJSIP/123@default-00 (None)               Down    ()\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "Local/123@default-00 (None)               Down    ()\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "SIP/alice@default-00 (None)               Down    ()\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "3 active channels\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "0 active calls\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "2 calls processed\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "Asterisk ending (0).\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.MockDefer(output)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AstMockObjectLeaked(AstMockOutput):</span><br><span style="color: hsl(120, 100%, 40%);">+    """mock cli output showing leaked channel"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def cli_exec(self, command):</span><br><span style="color: hsl(120, 100%, 40%);">+        """presume command is core show channels and generate output"""</span><br><span style="color: hsl(120, 100%, 40%);">+        output = ""</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "Channel              Location             State   Application(Data)\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "Local/123@default-00 (None)               Down    ()\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "0 active channels\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "0 active calls\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "2 calls processed\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        output += "Asterisk ending (0).\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.MockDefer(output)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class TestConfig(object):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Fake TestConfig object for unittest"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.class_type_name = "bogus"</span><br><span style="color: hsl(120, 100%, 40%);">+        self.config = {}</span><br><span style="color: hsl(120, 100%, 40%);">+        self.enabled = True</span><br><span style="color: hsl(120, 100%, 40%);">+        self.pass_expected = True</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class ChannelTestConditionUnitTest(unittest.TestCase):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Unit Tests for ChannelTestCondition"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_evaluate_inactive(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """test inactive channel condition"""</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = ChannelTestCondition(TestConfig())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(AstMockObjectInactive())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Passed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_evaluate_multiple_fail(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """test multiple channel condition"""</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = ChannelTestCondition(TestConfig())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(AstMockObjectMultiple())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_evaluate_multiple_fail2(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """test multiple channel condition"""</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = ChannelTestCondition(TestConfig())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.allowed_channels = 2</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(AstMockObjectMultiple())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_evaluate_multiple_pass(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """test multiple channel condition"""</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = ChannelTestCondition(TestConfig())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.allowed_channels = 3</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(AstMockObjectMultiple())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Passed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_evaluate_single_fail(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """test single channel condition"""</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = ChannelTestCondition(TestConfig())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(AstMockObjectSingle())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_evaluate_single_pass(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """test single channel condition"""</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = ChannelTestCondition(TestConfig())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.allowed_channels = 1</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(AstMockObjectSingle())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Passed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_evaluate_leaked(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """test leaked channel condition"""</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = ChannelTestCondition(TestConfig())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(AstMockObjectLeaked())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+if __name__ == "__main__":</span><br><span style="color: hsl(120, 100%, 40%);">+    main()</span><br><span>diff --git a/lib/python/asterisk/self_test/test_config.py b/lib/python/asterisk/self_test/test_config.py</span><br><span>new file mode 100755</span><br><span>index 0000000..cc88636</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/self_test/test_config.py</span><br><span>@@ -0,0 +1,83 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#!/usr/bin/env python</span><br><span style="color: hsl(120, 100%, 40%);">+"""Asterisk Configuration File Handling Unit Tests.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Copyright (C) 2010, Digium, Inc.</span><br><span style="color: hsl(120, 100%, 40%);">+Russell Bryant <russell@digium.com></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+the GNU General Public License Version 2.</span><br><span style="color: hsl(120, 100%, 40%);">+"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from harness_shared import main</span><br><span style="color: hsl(120, 100%, 40%);">+import unittest</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.config import ConfigFile</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class ConfigFileTests(unittest.TestCase):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Unit tests for ConfigFile"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_conf(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Test parsing a blob of config data"""</span><br><span style="color: hsl(120, 100%, 40%);">+        test = \</span><br><span style="color: hsl(120, 100%, 40%);">+            "; stuff\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "this line is invalid on purpose\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "[this is] also invalid]\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            ";-- comment --;\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            ";--   \n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "[this is commented out]\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "         --;\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "[foo]\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "a = b\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "  b =   a  \n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "this line is invalid on purpose\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            ";moo\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "c = d;asdadf;adfads;adsfasdf\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "  [bar]   ;asdfasdf\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "a-b=c-d\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "xyz=x|y|z\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "1234 => 4242,Example Mailbox,root@localhost,,var=val\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "[template](!)\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "foo=bar\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "exten => _NXX.,n,Wait(1)\n" \</span><br><span style="color: hsl(120, 100%, 40%);">+            "astetcdir => /etc/asterisk\n"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        conf = ConfigFile(filename=None, config_str=test)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(len(conf.categories), 3)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[0].name, "foo")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(conf.categories[0].template)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(len(conf.categories[0].options), 3)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[0].options[0][0], "a")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[0].options[0][1], "b")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[0].options[1][0], "b")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[0].options[1][1], "a")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[0].options[2][0], "c")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[0].options[2][1], "d")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[1].name, "bar")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(conf.categories[1].template)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(len(conf.categories[1].options), 3)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[1].options[0][0], "a-b")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[1].options[0][1], "c-d")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[1].options[1][0], "xyz")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[1].options[1][1], "x|y|z")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[1].options[2][0], "1234")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[1].options[2][1],</span><br><span style="color: hsl(120, 100%, 40%);">+                         "4242,Example Mailbox,root@localhost,,var=val")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[2].name, "template")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(conf.categories[2].template)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(len(conf.categories[2].options), 3)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[2].options[0][0], "foo")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[2].options[0][1], "bar")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[2].options[1][0], "exten")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[2].options[1][1],</span><br><span style="color: hsl(120, 100%, 40%);">+                         "_NXX.,n,Wait(1)")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[2].options[2][0], "astetcdir")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(conf.categories[2].options[2][1], "/etc/asterisk")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+if __name__ == "__main__":</span><br><span style="color: hsl(120, 100%, 40%);">+    main()</span><br><span>diff --git a/lib/python/asterisk/self_test/test_lock_test_condition.py b/lib/python/asterisk/self_test/test_lock_test_condition.py</span><br><span>new file mode 100755</span><br><span>index 0000000..6cda06a</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/self_test/test_lock_test_condition.py</span><br><span>@@ -0,0 +1,193 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#!/usr/bin/env python</span><br><span style="color: hsl(120, 100%, 40%);">+"""Held locks test condition unit tests</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Copyright (C) 2011-2012, Digium, Inc.</span><br><span style="color: hsl(120, 100%, 40%);">+Matt Jordan <mjordan@digium.com></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+the GNU General Public License Version 2.</span><br><span style="color: hsl(120, 100%, 40%);">+"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from harness_shared import AstMockOutput, ReadTestFile, main</span><br><span style="color: hsl(120, 100%, 40%);">+import unittest</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.lock_test_condition import LockSequence, LockObject, LockTestCondition</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AstMockObjectPassed(AstMockOutput):</span><br><span style="color: hsl(120, 100%, 40%);">+    """A lock output that passed"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Constructor"""</span><br><span style="color: hsl(120, 100%, 40%);">+        self.host = "127.0.0.2"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def cli_exec(self, command):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Fake out a CLI command execution"""</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.MockDeferFile("locks-pass.txt")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AstMockObjectFailure(AstMockOutput):</span><br><span style="color: hsl(120, 100%, 40%);">+    """A lock object that failed"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def cli_exec(self, command):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Fake out a CLI command execution"""</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.MockDeferFile("locks-fail.txt")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class TestConfig(object):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Fake TestConfig object"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """ Values here don't matter much - we just need to have something """</span><br><span style="color: hsl(120, 100%, 40%);">+        self.class_type_name = "asterisk.LockTestCondition.LockTestCondition"</span><br><span style="color: hsl(120, 100%, 40%);">+        self.pass_expected = True</span><br><span style="color: hsl(120, 100%, 40%);">+        self.type = "Post"</span><br><span style="color: hsl(120, 100%, 40%);">+        self.related_condition = ""</span><br><span style="color: hsl(120, 100%, 40%);">+        self.config = {}</span><br><span style="color: hsl(120, 100%, 40%);">+        self.enabled = True</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class LockTestConditionUnitTest(unittest.TestCase):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Unit tests for LockTestCondition"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_evaluate_failed(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Test a failed locking condition"""</span><br><span style="color: hsl(120, 100%, 40%);">+        ast = AstMockObjectFailure()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = LockTestCondition(TestConfig())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(ast)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_evaluate_pass(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Test a passed locking condition"""</span><br><span style="color: hsl(120, 100%, 40%);">+        ast = AstMockObjectPassed()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = LockTestCondition(TestConfig())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(ast)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Passed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_evaluate_multiple(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Test multiple results"""</span><br><span style="color: hsl(120, 100%, 40%);">+        ast1 = AstMockObjectPassed()</span><br><span style="color: hsl(120, 100%, 40%);">+        ast2 = AstMockObjectFailure()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = LockTestCondition(TestConfig())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(ast1)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(ast2)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class LockSequenceUnitTest(unittest.TestCase):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Tests for parsing a lock sequence"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_single_object_no_held_info(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Test a lock sequence with no waiting lock"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = LockSequence()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.parse_lock_sequence(ReadTestFile("locks-single-object-no-held-info.txt"))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_large_multiple_object(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Test with a waiting lock"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = LockSequence()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.parse_lock_sequence(ReadTestFile("locks-large-multiple-object.txt"))</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.thread_id, "0x449ec940")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.thread_name, "netconsole")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.thread_line, 1351)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.thread_file, "asterisk.c")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.thread_func, "listener")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(len(obj.locks) == 1)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].locked_file, "astobj2.c")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].locked_line, 657)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].locked_func, "internal_ao2_callback")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_single_object(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Test a lock held somewhere else"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = LockSequence()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.parse_lock_sequence(ReadTestFile("locks-single-object.txt"))</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.thread_id, "0x402c6940")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.thread_name, "do_monitor")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.thread_line, 25114)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.thread_file, "chan_sip.c")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.thread_func, "restart_monitor")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(len(obj.locks) == 1)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].locked_file, "channel.c")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].locked_line, 4304)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].locked_func, "ast_indicate_data")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].id, 0)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].type, "MUTEX")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].file, "chan_sip.c")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].line_number, 24629)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].func, "handle_request_do")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].name, "&netlock")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].addr, "0x2aaabe671a40")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].lock_count, 1)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(obj.locks[0].held)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(len(obj.locks[0].backtrace) == 3)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_multiple_objects_no_backtrace(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Test multiple locks with no backtrace"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = LockSequence()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.parse_lock_sequence(ReadTestFile("locks-multiple-objects-no-backtrace.txt"))</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.thread_id, "0x402c6940")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.thread_name, "do_monitor")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.thread_line, 25114)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.thread_file, "chan_sip.c")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.thread_func, "restart_monitor")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(len(obj.locks) == 3)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[2].locked_file, "channel.c")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[2].locked_line, 4304)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[2].locked_func, "ast_indicate_data")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].id, 0)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].type, "MUTEX")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].file, "chan_sip.c")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].line_number, 24629)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].func, "handle_request_do")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].name, "&netlock")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].addr, "0x2aaabe671a40")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.locks[0].lock_count, 1)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(obj.locks[0].held)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(len(obj.locks[0].backtrace) == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class LockObjectUnitTest(unittest.TestCase):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Unit tests for LockObject"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_no_backtrace(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Test creating a lock object with no thread backtrace"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        lock_line = "=== ---> Waiting for Lock #0 (sig_ss7.c): " + \</span><br><span style="color: hsl(120, 100%, 40%);">+                    "MUTEX 636 ss7_linkset &linkset->lock 0x2aaab8a6b588 (1)"</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = LockObject()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.parse_lock_information(lock_line)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.id, 0)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.type, "MUTEX")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.file, "sig_ss7.c")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.line_number, 636)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.func, "ss7_linkset")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.name, "&linkset->lock")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.addr, "0x2aaab8a6b588")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.lock_count, 1)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(obj.held)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(len(obj.backtrace) == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_with_backtrace(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Test creating a lock object with a thread backtrace"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = LockObject()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.parse_lock_information(ReadTestFile("locks-backtrace.txt"))</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.id, 1)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.type, "MUTEX")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.file, "astobj2.c")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.line_number, 657)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.func, "internal_ao2_callback")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.name, "c")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.addr, "0x2aaaac491f50")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.lock_count, 1)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(obj.held)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(len(obj.backtrace) == 19)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+if __name__ == "__main__":</span><br><span style="color: hsl(120, 100%, 40%);">+    main()</span><br><span>diff --git a/lib/python/asterisk/self_test/test2_matcher.py b/lib/python/asterisk/self_test/test_matcher.py</span><br><span>similarity index 100%</span><br><span>rename from lib/python/asterisk/self_test/test2_matcher.py</span><br><span>rename to lib/python/asterisk/self_test/test_matcher.py</span><br><span>diff --git a/lib/python/asterisk/self_test/test_sip_dialog_test_condition.py.txt b/lib/python/asterisk/self_test/test_sip_dialog_test_condition.py.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..717e183</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/self_test/test_sip_dialog_test_condition.py.txt</span><br><span>@@ -0,0 +1,426 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#!/usr/bin/env python</span><br><span style="color: hsl(120, 100%, 40%);">+"""Test condition for verifying SIP dialogs unit tests</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Copyright (C) 2011-2012, Digium, Inc.</span><br><span style="color: hsl(120, 100%, 40%);">+Matt Jordan <mjordan@digium.com></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+the GNU General Public License Version 2.</span><br><span style="color: hsl(120, 100%, 40%);">+"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from harness_shared import AstMockOutput, main</span><br><span style="color: hsl(120, 100%, 40%);">+import unittest</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.sip_dialog_test_condition import SipDialogPreTestCondition, \</span><br><span style="color: hsl(120, 100%, 40%);">+    SipDialogPostTestCondition</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class TestConfig(object):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Mock TestConfig object"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Constructor</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        Values here don't matter much - we just need to have something"""</span><br><span style="color: hsl(120, 100%, 40%);">+        self.type_name = ("asterisk.sip_dialog_test_condition." +</span><br><span style="color: hsl(120, 100%, 40%);">+                          "SipDialogPostTestCondition")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.pass_expected = True</span><br><span style="color: hsl(120, 100%, 40%);">+        self.type = "Post"</span><br><span style="color: hsl(120, 100%, 40%);">+        self.related_condition = ""</span><br><span style="color: hsl(120, 100%, 40%);">+        self.class_type_name = "sip_dialog_test_condition"</span><br><span style="color: hsl(120, 100%, 40%);">+        self.config = {}</span><br><span style="color: hsl(120, 100%, 40%);">+        self.enabled = True</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class TestConfigWithHistory(TestConfig):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Mock TestConfig object with history requirements"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Constructor"""</span><br><span style="color: hsl(120, 100%, 40%);">+        super(TestConfigWithHistory, self).__init__()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        self.config['history_requirements'] = []</span><br><span style="color: hsl(120, 100%, 40%);">+        self.config['history_requirements'].append('Hangup')</span><br><span style="color: hsl(120, 100%, 40%);">+        self.config['history_requirements'].append('NewChan')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AstMockObjectPostTestNoDestructionFail(AstMockOutput):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Mock out CLI execution from Asterisk instance</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    This mock object makes it appear as if a dialog failed to be destroyed</span><br><span style="color: hsl(120, 100%, 40%);">+    """</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Constructor"""</span><br><span style="color: hsl(120, 100%, 40%);">+        self.host = "127.0.0.6"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def cli_exec(self, command):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Mock CLI execution/response"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        output = ""</span><br><span style="color: hsl(120, 100%, 40%);">+        if command == "sip show objects":</span><br><span style="color: hsl(120, 100%, 40%);">+            output = "-= Peer objects: 4 static, 0 realtime, 0 autocreate =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7001\ntype: peer\nobjflags: 0\nrefcount: 1\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Peer objects by IP =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Registry objects: 0 =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Dialog objects:\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 2ec048aa4ed1239664f6408f0c5044c4@127.0.0.2:5060\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "type: dialog\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "objflags: 0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "refcount: 2\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 2ec048aa4ed1239664f6408f0c5044c5@127.0.0.2:5060\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "type: dialog\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "objflags: 0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "refcount: 2\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        elif command == "sip show history 2ec048aa4ed1239664f6408f0c5044c4@127.0.0.2:5060":</span><br><span style="color: hsl(120, 100%, 40%);">+            output = "* SIP Call\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "1. NewChan         Channel SIP/ast1-00000002 - from 2ec048aa4ed1239664f6408f0c5044\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "2. TxReqRel        INVITE / 102 INVITE - INVITE\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "3. Rx              SIP/2.0 / 102 INVITE / 100 Trying\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "4. Rx              SIP/2.0 / 102 INVITE / 200 OK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "5. TxReq           ACK / 102 ACK - ACK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "6. Rx              BYE / 102 BYE / sip:ast2@127.0.0.2:5060\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "7. RTCPaudio       Quality:ssrc=28249381;themssrc=485141946;lp=0;rxjitter=0.000029\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "8. RTCPaudioJitter Quality:minrxjitter=0.000000;maxrxjitter=0.000000;avgrxjitter=0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "9. RTCPaudioLoss   Quality:minrxlost=0.000000;maxrxlost=0.000000;avgrxlost=0.00000\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "10. RTCPaudioRTT    Quality:minrtt=0.000000;maxrtt=0.000000;avgrtt=0.000000;stdevrt\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "11. SchedDestroy    32000 ms\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "12. TxResp          SIP/2.0 / 102 BYE - 200 OK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "13. Hangup          Cause Normal Clearing\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        elif command == "sip show history 2ec048aa4ed1239664f6408f0c5044c5@127.0.0.2:5060":</span><br><span style="color: hsl(120, 100%, 40%);">+            output = "* SIP Call\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "1. NewChan         Channel SIP/ast1-00000002 - from 2ec048aa4ed1239664f6408f0c5044\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "2. TxReqRel        INVITE / 102 INVITE - INVITE\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "3. Rx              SIP/2.0 / 102 INVITE / 100 Trying\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "4. Rx              SIP/2.0 / 102 INVITE / 200 OK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "5. TxReq           ACK / 102 ACK - ACK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "6. Rx              BYE / 102 BYE / sip:ast2@127.0.0.2:5060\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "7. RTCPaudio       Quality:ssrc=28249381;themssrc=485141946;lp=0;rxjitter=0.000029\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "8. RTCPaudioJitter Quality:minrxjitter=0.000000;maxrxjitter=0.000000;avgrxjitter=0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "9. RTCPaudioLoss   Quality:minrxlost=0.000000;maxrxlost=0.000000;avgrxlost=0.00000\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "10. RTCPaudioRTT    Quality:minrtt=0.000000;maxrtt=0.000000;avgrtt=0.000000;stdevrt\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "11. TxResp          SIP/2.0 / 102 BYE - 200 OK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "12. Hangup          Cause Normal Clearing\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.MockDefer(output)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AstMockObjectPostTestNoHangupFail(AstMockOutput):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Mock out CLI execution from Asterisk instance</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    This mock object makes it appear as if a channel failed to hangup</span><br><span style="color: hsl(120, 100%, 40%);">+    """</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Constructor"""</span><br><span style="color: hsl(120, 100%, 40%);">+        self.host = "127.0.0.5"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def cli_exec(self, command):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Mock CLI execution/response"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        output = ""</span><br><span style="color: hsl(120, 100%, 40%);">+        if command == "sip show objects":</span><br><span style="color: hsl(120, 100%, 40%);">+            output = "-= Peer objects: 4 static, 0 realtime, 0 autocreate =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7001\ntype: peer\nobjflags: 0\nrefcount: 1\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Peer objects by IP =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Registry objects: 0 =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Dialog objects:\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 2ec048aa4ed1239664f6408f0c5044c4@127.0.0.2:5060\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "type: dialog\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "objflags: 0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "refcount: 2\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 2ec048aa4ed1239664f6408f0c5044c5@127.0.0.2:5060\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "type: dialog\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "objflags: 0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "refcount: 2\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        elif command == "sip show history 2ec048aa4ed1239664f6408f0c5044c4@127.0.0.2:5060":</span><br><span style="color: hsl(120, 100%, 40%);">+            output = "* SIP Call\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "1. NewChan         Channel SIP/ast1-00000002 - from 2ec048aa4ed1239664f6408f0c5044\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "2. TxReqRel        INVITE / 102 INVITE - INVITE\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "3. Rx              SIP/2.0 / 102 INVITE / 100 Trying\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "4. Rx              SIP/2.0 / 102 INVITE / 200 OK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "5. TxReq           ACK / 102 ACK - ACK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "6. Rx              BYE / 102 BYE / sip:ast2@127.0.0.2:5060\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "7. RTCPaudio       Quality:ssrc=28249381;themssrc=485141946;lp=0;rxjitter=0.000029\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "8. RTCPaudioJitter Quality:minrxjitter=0.000000;maxrxjitter=0.000000;avgrxjitter=0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "9. RTCPaudioLoss   Quality:minrxlost=0.000000;maxrxlost=0.000000;avgrxlost=0.00000\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "10. RTCPaudioRTT    Quality:minrtt=0.000000;maxrtt=0.000000;avgrtt=0.000000;stdevrt\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "11. SchedDestroy    32000 ms\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "12. TxResp          SIP/2.0 / 102 BYE - 200 OK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        elif command == "sip show history 2ec048aa4ed1239664f6408f0c5044c5@127.0.0.2:5060":</span><br><span style="color: hsl(120, 100%, 40%);">+            output = "* SIP Call\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "1. NewChan         Channel SIP/ast1-00000002 - from 2ec048aa4ed1239664f6408f0c5044\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "2. TxReqRel        INVITE / 102 INVITE - INVITE\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "3. Rx              SIP/2.0 / 102 INVITE / 100 Trying\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "4. Rx              SIP/2.0 / 102 INVITE / 200 OK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "5. TxReq           ACK / 102 ACK - ACK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "6. Rx              BYE / 102 BYE / sip:ast2@127.0.0.2:5060\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "7. RTCPaudio       Quality:ssrc=28249381;themssrc=485141946;lp=0;rxjitter=0.000029\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "8. RTCPaudioJitter Quality:minrxjitter=0.000000;maxrxjitter=0.000000;avgrxjitter=0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "9. RTCPaudioLoss   Quality:minrxlost=0.000000;maxrxlost=0.000000;avgrxlost=0.00000\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "10. RTCPaudioRTT    Quality:minrtt=0.000000;maxrtt=0.000000;avgrtt=0.000000;stdevrt\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "11. SchedDestroy    32000 ms\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "12. TxResp          SIP/2.0 / 102 BYE - 200 OK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "13. Hangup          Cause Normal Clearing\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.MockDefer(output)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AstMockObjectPostTestNoDialogsPass(AstMockOutput):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Mock out CLI execution from Asterisk instance</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    This mock object makes it appear as if there were no dialogs, which is okay</span><br><span style="color: hsl(120, 100%, 40%);">+    """</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Constructor"""</span><br><span style="color: hsl(120, 100%, 40%);">+        self.host = "127.0.0.4"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def cli_exec(self, command):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Mock CLI execution/response"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        output = ""</span><br><span style="color: hsl(120, 100%, 40%);">+        if command == "sip show objects":</span><br><span style="color: hsl(120, 100%, 40%);">+            output = "-= Peer objects: 4 static, 0 realtime, 0 autocreate =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7001\ntype: peer\nobjflags: 0\nrefcount: 1\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Peer objects by IP =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Registry objects: 0 =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Dialog objects:\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.MockDefer(output)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AstMockObjectPostTestPass(AstMockOutput):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Mock out CLI execution from Asterisk instance</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    This mock object provides two dialogs with acceptable history</span><br><span style="color: hsl(120, 100%, 40%);">+    """</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Constructor"""</span><br><span style="color: hsl(120, 100%, 40%);">+        self.host = "127.0.0.3"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def cli_exec(self, command):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Mock CLI execution/response"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        output = ""</span><br><span style="color: hsl(120, 100%, 40%);">+        if command == "sip show objects":</span><br><span style="color: hsl(120, 100%, 40%);">+            output = "-= Peer objects: 4 static, 0 realtime, 0 autocreate =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7001\ntype: peer\nobjflags: 0\nrefcount: 1\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Peer objects by IP =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Registry objects: 0 =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Dialog objects:\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 2ec048aa4ed1239664f6408f0c5044c4@127.0.0.2:5060\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "type: dialog\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "objflags: 0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "refcount: 2\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 2ec048aa4ed1239664f6408f0c5044c5@127.0.0.2:5060\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "type: dialog\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "objflags: 0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "refcount: 2\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        elif command == "sip show history 2ec048aa4ed1239664f6408f0c5044c4@127.0.0.2:5060":</span><br><span style="color: hsl(120, 100%, 40%);">+            output = "* SIP Call\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "1. NewChan         Channel SIP/ast1-00000002 - from 2ec048aa4ed1239664f6408f0c5044\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "2. TxReqRel        INVITE / 102 INVITE - INVITE\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "3. Rx              SIP/2.0 / 102 INVITE / 100 Trying\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "4. Rx              SIP/2.0 / 102 INVITE / 200 OK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "5. TxReq           ACK / 102 ACK - ACK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "6. Rx              BYE / 102 BYE / sip:ast2@127.0.0.2:5060\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "7. RTCPaudio       Quality:ssrc=28249381;themssrc=485141946;lp=0;rxjitter=0.000029\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "8. RTCPaudioJitter Quality:minrxjitter=0.000000;maxrxjitter=0.000000;avgrxjitter=0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "9. RTCPaudioLoss   Quality:minrxlost=0.000000;maxrxlost=0.000000;avgrxlost=0.00000\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "10. RTCPaudioRTT    Quality:minrtt=0.000000;maxrtt=0.000000;avgrtt=0.000000;stdevrt\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "11. SchedDestroy    32000 ms\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "12. TxResp          SIP/2.0 / 102 BYE - 200 OK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "13. Hangup          Cause Normal Clearing\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        elif command == "sip show history 2ec048aa4ed1239664f6408f0c5044c5@127.0.0.2:5060":</span><br><span style="color: hsl(120, 100%, 40%);">+            output = "* SIP Call\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "1. NewChan         Channel SIP/ast1-00000002 - from 2ec048aa4ed1239664f6408f0c5044\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "2. TxReqRel        INVITE / 102 INVITE - INVITE\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "3. Rx              SIP/2.0 / 102 INVITE / 100 Trying\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "4. Rx              SIP/2.0 / 102 INVITE / 200 OK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "5. TxReq           ACK / 102 ACK - ACK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "6. Rx              BYE / 102 BYE / sip:ast2@127.0.0.2:5060\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "7. RTCPaudio       Quality:ssrc=28249381;themssrc=485141946;lp=0;rxjitter=0.000029\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "8. RTCPaudioJitter Quality:minrxjitter=0.000000;maxrxjitter=0.000000;avgrxjitter=0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "9. RTCPaudioLoss   Quality:minrxlost=0.000000;maxrxlost=0.000000;avgrxlost=0.00000\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "10. RTCPaudioRTT    Quality:minrtt=0.000000;maxrtt=0.000000;avgrtt=0.000000;stdevrt\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "11. SchedDestroy    32000 ms\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "12. TxResp          SIP/2.0 / 102 BYE - 200 OK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "13. Hangup          Cause Normal Clearing\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.MockDefer(output)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AstMockObjectPreTestFail(AstMockOutput):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Mock out CLI execution from Asterisk instance</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    This mock object provides history during a pre-test call, which is wrong</span><br><span style="color: hsl(120, 100%, 40%);">+    """</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Constructor"""</span><br><span style="color: hsl(120, 100%, 40%);">+        self.host = "127.0.0.2"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def cli_exec(self, command):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Mock CLI execution/response"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        output = ""</span><br><span style="color: hsl(120, 100%, 40%);">+        if command == "sip show objects":</span><br><span style="color: hsl(120, 100%, 40%);">+            output = "-= Peer objects: 4 static, 0 realtime, 0 autocreate =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7001\ntype: peer\nobjflags: 0\nrefcount: 1\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Peer objects by IP =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Registry objects: 0 =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Dialog objects:\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 2ec048aa4ed1239664f6408f0c5044c4@127.0.0.2:5060\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "type: dialog\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "objflags: 0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "refcount: 2\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        elif command == "sip show history 2ec048aa4ed1239664f6408f0c5044c4@127.0.0.2:5060":</span><br><span style="color: hsl(120, 100%, 40%);">+            output = "* SIP Call\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "1. NewChan         Channel SIP/ast1-00000002 - from 2ec048aa4ed1239664f6408f0c5044\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "2. TxReqRel        INVITE / 102 INVITE - INVITE\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "3. Rx              SIP/2.0 / 102 INVITE / 100 Trying\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "4. Rx              SIP/2.0 / 102 INVITE / 200 OK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "5. TxReq           ACK / 102 ACK - ACK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "6. Rx              BYE / 102 BYE / sip:ast2@127.0.0.2:5060\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "7. RTCPaudio       Quality:ssrc=28249381;themssrc=485141946;lp=0;rxjitter=0.000029\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "8. RTCPaudioJitter Quality:minrxjitter=0.000000;maxrxjitter=0.000000;avgrxjitter=0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "9. RTCPaudioLoss   Quality:minrxlost=0.000000;maxrxlost=0.000000;avgrxlost=0.00000\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "10. RTCPaudioRTT    Quality:minrtt=0.000000;maxrtt=0.000000;avgrtt=0.000000;stdevrt\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "11. SchedDestroy    32000 ms\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "12. TxResp          SIP/2.0 / 102 BYE - 200 OK\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "13. Hangup          Cause Normal Clearing\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.MockDefer(output)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class AstMockObjectPreTestPass(AstMockOutput):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Mock out CLI execution from Asterisk instance</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    This mock object provides no history during a pre-test call, which is the</span><br><span style="color: hsl(120, 100%, 40%);">+    expected state</span><br><span style="color: hsl(120, 100%, 40%);">+    """</span><br><span style="color: hsl(120, 100%, 40%);">+    def cli_exec(self, command):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Mock CLI execution/response"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        output = ""</span><br><span style="color: hsl(120, 100%, 40%);">+        if command == "sip show objects":</span><br><span style="color: hsl(120, 100%, 40%);">+            output = "-= Peer objects: 4 static, 0 realtime, 0 autocreate =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7001\ntype: peer\nobjflags: 0\nrefcount: 1\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Peer objects by IP =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Registry objects: 0 =-\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            output += "-= Dialog objects:\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        elif command == "sip show history":</span><br><span style="color: hsl(120, 100%, 40%);">+            output = "\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.MockDefer(output)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+@unittest.skip("sip_dialog_test_condition.py is broken")</span><br><span style="color: hsl(120, 100%, 40%);">+class SipDialogTestConditionUnitTest(unittest.TestCase):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Unit tests for SipDialogTestCondition objects"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_pre_test_pass(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Verify that acceptable pre-test output passes"""</span><br><span style="color: hsl(120, 100%, 40%);">+        ast = AstMockObjectPreTestPass()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = SipDialogPreTestCondition(TestConfig())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(ast)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Passed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_pre_test_fail(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Verify that unacceptable pre-test output fails"""</span><br><span style="color: hsl(120, 100%, 40%);">+        ast = AstMockObjectPreTestFail()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = SipDialogPreTestCondition(TestConfig())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(ast)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_pre_test_fail_multi_asterisk(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Verify that pre-test output from multiple sources fails when one</span><br><span style="color: hsl(120, 100%, 40%);">+        of those sources is bad"""</span><br><span style="color: hsl(120, 100%, 40%);">+        ast1 = AstMockObjectPreTestFail()</span><br><span style="color: hsl(120, 100%, 40%);">+        ast2 = AstMockObjectPreTestPass()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = SipDialogPreTestCondition(TestConfig())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(ast1)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(ast2)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_post_test_pass(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Verify nominal post-test output"""</span><br><span style="color: hsl(120, 100%, 40%);">+        ast = AstMockObjectPostTestPass()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = SipDialogPostTestCondition(TestConfigWithHistory())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(ast)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Passed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_post_test_no_dialog_pass(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Verify nominal post-test output with no dialogs passes"""</span><br><span style="color: hsl(120, 100%, 40%);">+        ast = AstMockObjectPostTestNoDialogsPass()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = SipDialogPostTestCondition(TestConfig())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(ast)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Passed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_post_test_no_hangup_fail(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Verify no hangup detection is caught and results in a failure"""</span><br><span style="color: hsl(120, 100%, 40%);">+        ast = AstMockObjectPostTestNoHangupFail()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = SipDialogPostTestCondition(TestConfigWithHistory())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(ast)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_post_test_no_destruction_fail(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Verify no destruction is caught and results in a failure"""</span><br><span style="color: hsl(120, 100%, 40%);">+        ast = AstMockObjectPostTestNoDestructionFail()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = SipDialogPostTestCondition(TestConfigWithHistory())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(ast)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_post_test_multi_asterisk_fail(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Test multiple instances of Asterisk where a single failure causes</span><br><span style="color: hsl(120, 100%, 40%);">+        a failure in the overall result"""</span><br><span style="color: hsl(120, 100%, 40%);">+        ast1 = AstMockObjectPostTestNoHangupFail()</span><br><span style="color: hsl(120, 100%, 40%);">+        ast2 = AstMockObjectPostTestNoDestructionFail()</span><br><span style="color: hsl(120, 100%, 40%);">+        ast3 = AstMockObjectPostTestNoDialogsPass()</span><br><span style="color: hsl(120, 100%, 40%);">+        ast4 = AstMockObjectPostTestPass()</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = SipDialogPostTestCondition(TestConfigWithHistory())</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(ast1)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(ast2)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(ast3)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.register_asterisk_instance(ast4)</span><br><span style="color: hsl(120, 100%, 40%);">+        obj.evaluate()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+if __name__ == "__main__":</span><br><span style="color: hsl(120, 100%, 40%);">+    main()</span><br><span>diff --git a/lib/python/asterisk/self_test/test_sippversion.py b/lib/python/asterisk/self_test/test_sippversion.py</span><br><span>new file mode 100755</span><br><span>index 0000000..8c3d933</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/self_test/test_sippversion.py</span><br><span>@@ -0,0 +1,221 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#!/usr/bin/env python</span><br><span style="color: hsl(120, 100%, 40%);">+"""SIPp Version String Handling Tests</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Copyright (C) 2010, Digium, Inc.</span><br><span style="color: hsl(120, 100%, 40%);">+Paul Belanger <pabelanger@digium.com></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+the GNU General Public License Version 2.</span><br><span style="color: hsl(120, 100%, 40%);">+"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from harness_shared import main</span><br><span style="color: hsl(120, 100%, 40%);">+import unittest</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.sippversion import SIPpVersion</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class SIPpVersionTests(unittest.TestCase):</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_version(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v = SIPpVersion("v3.2", None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(str(v), "v3.2")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.concept, "v3")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.major, "2")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.minor, None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(v.tls)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(v.pcap)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_version2(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v = SIPpVersion("v2.0.1", None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(str(v), "v2.0.1")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.concept, "v2")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.major, "0")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.minor, "1")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(v.tls)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(v.pcap)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_version3(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v = SIPpVersion("v3.1", "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(str(v), "v3.1-TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.concept, "v3")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.major, "1")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.minor, None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v.tls)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(v.pcap)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_version4(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v = SIPpVersion("v2.0.1", "TLS-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(str(v), "v2.0.1-TLS-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.concept, "v2")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.major, "0")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.minor, "1")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v.tls)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v.pcap)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_version5(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v = SIPpVersion("v3.2", "PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(str(v), "v3.2-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.concept, "v3")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.major, "2")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.minor, None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(v.tls)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v.pcap)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_version6(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v = SIPpVersion(None, "PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(str(v), "PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.concept, None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.major, None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.minor, None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(v.tls)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v.pcap)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_version7(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v = SIPpVersion(None, "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(str(v), "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.concept, None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.major, None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.minor, None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v.tls)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(v.pcap)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_version8(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v = SIPpVersion(None, "TLS-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(str(v), "TLS-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.concept, None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.major, None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertEqual(v.minor, None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v.tls)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v.pcap)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.2", None)</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.1", None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 > v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp2(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v2.0.1", None)</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.1", None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 < v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp3(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.1", None)</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.1", None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 == v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp4(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.1", None)</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.1", None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(v1 != v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp5(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.1", "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.1", "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 == v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp6(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion(None, "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion(None, "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 == v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp7(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v2.0.1", None)</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v2.0.1", None)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 == v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp8(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.2", "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.2", "PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 != v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp9(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion(None, "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion(None, "PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 != v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp10(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.2", "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.2", "PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(v1 == v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp11(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion(None, "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion(None, "PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(v1 == v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp12(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.2", "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 >= v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp13(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion(None, "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 >= v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp14(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.2", "PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 >= v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp15(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion(None, "PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 >= v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp16(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.2", "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 != v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp17(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion(None, "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 != v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp18(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.2", "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(v1 == v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp19(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion(None, "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(v1 == v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp20(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.1", "PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.0", "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 > v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp21(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v2.0.1", "PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 >= v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp22(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.1", "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.1", "PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(v1 > v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp23(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.1", "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.1", "PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(v1 < v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp24(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.2", "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.1", "TLS-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 >= v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp25(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v3.1", "TLS-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.2", "PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertTrue(v1 <= v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_cmp26(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        v1 = SIPpVersion("v2.0.1", "TLS-PCAP")</span><br><span style="color: hsl(120, 100%, 40%);">+        v2 = SIPpVersion("v3.0", "TLS")</span><br><span style="color: hsl(120, 100%, 40%);">+        self.assertFalse(v1 >= v2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+if __name__ == "__main__":</span><br><span style="color: hsl(120, 100%, 40%);">+    main()</span><br><span>diff --git a/lib/python/asterisk/self_test/test_utils_socket.py b/lib/python/asterisk/self_test/test_utils_socket.py</span><br><span>index 3a0c33d..34672c6 100755</span><br><span>--- a/lib/python/asterisk/self_test/test_utils_socket.py</span><br><span>+++ b/lib/python/asterisk/self_test/test_utils_socket.py</span><br><span>@@ -10,19 +10,12 @@</span><br><span> the GNU General Public License Version 2.</span><br><span> """</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-import logging</span><br><span style="color: hsl(0, 100%, 40%);">-import sys</span><br><span style="color: hsl(120, 100%, 40%);">+from harness_shared import main</span><br><span> import unittest</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> from socket import SOCK_STREAM, SOCK_DGRAM, AF_INET, AF_INET6</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-sys.path.append('lib/python')  # noqa</span><br><span> from asterisk.utils_socket import Ports, PortError, get_available_port, MIN_PORT</span><br><span> </span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-LOGGER = logging.getLogger(__name__)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> class PortTests(unittest.TestCase):</span><br><span>     """Unit tests for port availability and reservations."""</span><br><span> </span><br><span>@@ -169,7 +162,4 @@</span><br><span> </span><br><span> if __name__ == "__main__":</span><br><span>     """Run the unit tests"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    logging.basicConfig(stream=sys.stdout, level=logging.DEBUG,</span><br><span style="color: hsl(0, 100%, 40%);">-                        format="%(module)s:%(lineno)d - %(message)s")</span><br><span style="color: hsl(0, 100%, 40%);">-    unittest.main()</span><br><span style="color: hsl(120, 100%, 40%);">+    main()</span><br><span>diff --git a/lib/python/asterisk/sip_channel_test_condition.py b/lib/python/asterisk/sip_channel_test_condition.py</span><br><span>index 150d90f..0f265f0 100644</span><br><span>--- a/lib/python/asterisk/sip_channel_test_condition.py</span><br><span>+++ b/lib/python/asterisk/sip_channel_test_condition.py</span><br><span>@@ -10,7 +10,7 @@</span><br><span> """</span><br><span> </span><br><span> from twisted.internet import defer</span><br><span style="color: hsl(0, 100%, 40%);">-from test_conditions import TestCondition</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_conditions import TestCondition</span><br><span> </span><br><span> </span><br><span> class SipChannelTestCondition(TestCondition):</span><br><span>diff --git a/lib/python/asterisk/sip_dialog_test_condition.py b/lib/python/asterisk/sip_dialog_test_condition.py</span><br><span>index 4c75710..619a388 100644</span><br><span>--- a/lib/python/asterisk/sip_dialog_test_condition.py</span><br><span>+++ b/lib/python/asterisk/sip_dialog_test_condition.py</span><br><span>@@ -10,9 +10,8 @@</span><br><span> </span><br><span> import logging</span><br><span> import logging.config</span><br><span style="color: hsl(0, 100%, 40%);">-import unittest</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from test_conditions import TestCondition</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_conditions import TestCondition</span><br><span> from twisted.internet import defer</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span>@@ -227,422 +226,3 @@</span><br><span>         self._counter = -1</span><br><span>         __get_dialogs()</span><br><span>         return self._finished_deferred</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class TestConfig(object):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Mock TestConfig object"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Constructor</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        Values here don't matter much - we just need to have something"""</span><br><span style="color: hsl(0, 100%, 40%);">-        self.type_name = ("asterisk.sip_dialog_test_condition." +</span><br><span style="color: hsl(0, 100%, 40%);">-                          "SipDialogPostTestCondition")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.pass_expected = True</span><br><span style="color: hsl(0, 100%, 40%);">-        self.type = "Post"</span><br><span style="color: hsl(0, 100%, 40%);">-        self.related_condition = ""</span><br><span style="color: hsl(0, 100%, 40%);">-        self.config = {}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class TestConfigWithHistory(TestConfig):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Mock TestConfig object with history requirements"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Constructor"""</span><br><span style="color: hsl(0, 100%, 40%);">-        super(TestConfigWithHistory, self).__init__()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        self.config['history_requirements'] = []</span><br><span style="color: hsl(0, 100%, 40%);">-        self.config['history_requirements'].append('Hangup')</span><br><span style="color: hsl(0, 100%, 40%);">-        self.config['history_requirements'].append('NewChan')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class AstMockObjectPostTestNoDestructionFail(object):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Mock out CLI execution from Asterisk instance</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    This mock object makes it appear as if a dialog failed to be destroyed</span><br><span style="color: hsl(0, 100%, 40%);">-    """</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Constructor"""</span><br><span style="color: hsl(0, 100%, 40%);">-        self.host = "127.0.0.6"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def cli_exec(self, command):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Mock CLI execution/response"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        ret_string = ""</span><br><span style="color: hsl(0, 100%, 40%);">-        if command == "sip show objects":</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string = "-= Peer objects: 4 static, 0 realtime, 0 autocreate =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7001\ntype: peer\nobjflags: 0\nrefcount: 1\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Peer objects by IP =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Registry objects: 0 =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Dialog objects:\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 2ec048aa4ed1239664f6408f0c5044c4@127.0.0.2:5060\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "type: dialog\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "objflags: 0\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "refcount: 2\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 2ec048aa4ed1239664f6408f0c5044c5@127.0.0.2:5060\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "type: dialog\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "objflags: 0\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "refcount: 2\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        elif command == "sip show history 2ec048aa4ed1239664f6408f0c5044c4@127.0.0.2:5060":</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string = "* SIP Call\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "1. NewChan         Channel SIP/ast1-00000002 - from 2ec048aa4ed1239664f6408f0c5044\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "2. TxReqRel        INVITE / 102 INVITE - INVITE\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "3. Rx              SIP/2.0 / 102 INVITE / 100 Trying\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "4. Rx              SIP/2.0 / 102 INVITE / 200 OK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "5. TxReq           ACK / 102 ACK - ACK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "6. Rx              BYE / 102 BYE / sip:ast2@127.0.0.2:5060\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "7. RTCPaudio       Quality:ssrc=28249381;themssrc=485141946;lp=0;rxjitter=0.000029\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "8. RTCPaudioJitter Quality:minrxjitter=0.000000;maxrxjitter=0.000000;avgrxjitter=0\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "9. RTCPaudioLoss   Quality:minrxlost=0.000000;maxrxlost=0.000000;avgrxlost=0.00000\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "10. RTCPaudioRTT    Quality:minrtt=0.000000;maxrtt=0.000000;avgrtt=0.000000;stdevrt\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "11. SchedDestroy    32000 ms\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "12. TxResp          SIP/2.0 / 102 BYE - 200 OK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "13. Hangup          Cause Normal Clearing\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        elif command == "sip show history 2ec048aa4ed1239664f6408f0c5044c5@127.0.0.2:5060":</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string = "* SIP Call\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "1. NewChan         Channel SIP/ast1-00000002 - from 2ec048aa4ed1239664f6408f0c5044\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "2. TxReqRel        INVITE / 102 INVITE - INVITE\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "3. Rx              SIP/2.0 / 102 INVITE / 100 Trying\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "4. Rx              SIP/2.0 / 102 INVITE / 200 OK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "5. TxReq           ACK / 102 ACK - ACK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "6. Rx              BYE / 102 BYE / sip:ast2@127.0.0.2:5060\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "7. RTCPaudio       Quality:ssrc=28249381;themssrc=485141946;lp=0;rxjitter=0.000029\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "8. RTCPaudioJitter Quality:minrxjitter=0.000000;maxrxjitter=0.000000;avgrxjitter=0\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "9. RTCPaudioLoss   Quality:minrxlost=0.000000;maxrxlost=0.000000;avgrxlost=0.00000\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "10. RTCPaudioRTT    Quality:minrtt=0.000000;maxrtt=0.000000;avgrtt=0.000000;stdevrt\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "11. TxResp          SIP/2.0 / 102 BYE - 200 OK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "12. Hangup          Cause Normal Clearing\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        return ret_string</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class AstMockObjectPostTestNoHangupFail(object):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Mock out CLI execution from Asterisk instance</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    This mock object makes it appear as if a channel failed to hangup</span><br><span style="color: hsl(0, 100%, 40%);">-    """</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Constructor"""</span><br><span style="color: hsl(0, 100%, 40%);">-        self.host = "127.0.0.5"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def cli_exec(self, command):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Mock CLI execution/response"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        ret_string = ""</span><br><span style="color: hsl(0, 100%, 40%);">-        if command == "sip show objects":</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string = "-= Peer objects: 4 static, 0 realtime, 0 autocreate =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7001\ntype: peer\nobjflags: 0\nrefcount: 1\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Peer objects by IP =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Registry objects: 0 =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Dialog objects:\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 2ec048aa4ed1239664f6408f0c5044c4@127.0.0.2:5060\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "type: dialog\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "objflags: 0\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "refcount: 2\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 2ec048aa4ed1239664f6408f0c5044c5@127.0.0.2:5060\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "type: dialog\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "objflags: 0\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "refcount: 2\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        elif command == "sip show history 2ec048aa4ed1239664f6408f0c5044c4@127.0.0.2:5060":</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string = "* SIP Call\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "1. NewChan         Channel SIP/ast1-00000002 - from 2ec048aa4ed1239664f6408f0c5044\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "2. TxReqRel        INVITE / 102 INVITE - INVITE\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "3. Rx              SIP/2.0 / 102 INVITE / 100 Trying\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "4. Rx              SIP/2.0 / 102 INVITE / 200 OK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "5. TxReq           ACK / 102 ACK - ACK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "6. Rx              BYE / 102 BYE / sip:ast2@127.0.0.2:5060\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "7. RTCPaudio       Quality:ssrc=28249381;themssrc=485141946;lp=0;rxjitter=0.000029\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "8. RTCPaudioJitter Quality:minrxjitter=0.000000;maxrxjitter=0.000000;avgrxjitter=0\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "9. RTCPaudioLoss   Quality:minrxlost=0.000000;maxrxlost=0.000000;avgrxlost=0.00000\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "10. RTCPaudioRTT    Quality:minrtt=0.000000;maxrtt=0.000000;avgrtt=0.000000;stdevrt\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "11. SchedDestroy    32000 ms\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "12. TxResp          SIP/2.0 / 102 BYE - 200 OK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        elif command == "sip show history 2ec048aa4ed1239664f6408f0c5044c5@127.0.0.2:5060":</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string = "* SIP Call\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "1. NewChan         Channel SIP/ast1-00000002 - from 2ec048aa4ed1239664f6408f0c5044\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "2. TxReqRel        INVITE / 102 INVITE - INVITE\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "3. Rx              SIP/2.0 / 102 INVITE / 100 Trying\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "4. Rx              SIP/2.0 / 102 INVITE / 200 OK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "5. TxReq           ACK / 102 ACK - ACK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "6. Rx              BYE / 102 BYE / sip:ast2@127.0.0.2:5060\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "7. RTCPaudio       Quality:ssrc=28249381;themssrc=485141946;lp=0;rxjitter=0.000029\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "8. RTCPaudioJitter Quality:minrxjitter=0.000000;maxrxjitter=0.000000;avgrxjitter=0\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "9. RTCPaudioLoss   Quality:minrxlost=0.000000;maxrxlost=0.000000;avgrxlost=0.00000\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "10. RTCPaudioRTT    Quality:minrtt=0.000000;maxrtt=0.000000;avgrtt=0.000000;stdevrt\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "11. SchedDestroy    32000 ms\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "12. TxResp          SIP/2.0 / 102 BYE - 200 OK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "13. Hangup          Cause Normal Clearing\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        return ret_string</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class AstMockObjectPostTestNoDialogsPass(object):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Mock out CLI execution from Asterisk instance</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    This mock object makes it appear as if there were no dialogs, which is okay</span><br><span style="color: hsl(0, 100%, 40%);">-    """</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Constructor"""</span><br><span style="color: hsl(0, 100%, 40%);">-        self.host = "127.0.0.4"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def cli_exec(self, command):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Mock CLI execution/response"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        ret_string = ""</span><br><span style="color: hsl(0, 100%, 40%);">-        if command == "sip show objects":</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string = "-= Peer objects: 4 static, 0 realtime, 0 autocreate =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7001\ntype: peer\nobjflags: 0\nrefcount: 1\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Peer objects by IP =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Registry objects: 0 =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Dialog objects:\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        return ret_string</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class AstMockObjectPostTestPass(object):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Mock out CLI execution from Asterisk instance</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    This mock object provides two dialogs with acceptable history</span><br><span style="color: hsl(0, 100%, 40%);">-    """</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Constructor"""</span><br><span style="color: hsl(0, 100%, 40%);">-        self.host = "127.0.0.3"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def cli_exec(self, command):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Mock CLI execution/response"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        ret_string = ""</span><br><span style="color: hsl(0, 100%, 40%);">-        if command == "sip show objects":</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string = "-= Peer objects: 4 static, 0 realtime, 0 autocreate =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7001\ntype: peer\nobjflags: 0\nrefcount: 1\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Peer objects by IP =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Registry objects: 0 =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Dialog objects:\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 2ec048aa4ed1239664f6408f0c5044c4@127.0.0.2:5060\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "type: dialog\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "objflags: 0\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "refcount: 2\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 2ec048aa4ed1239664f6408f0c5044c5@127.0.0.2:5060\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "type: dialog\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "objflags: 0\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "refcount: 2\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        elif command == "sip show history 2ec048aa4ed1239664f6408f0c5044c4@127.0.0.2:5060":</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string = "* SIP Call\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "1. NewChan         Channel SIP/ast1-00000002 - from 2ec048aa4ed1239664f6408f0c5044\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "2. TxReqRel        INVITE / 102 INVITE - INVITE\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "3. Rx              SIP/2.0 / 102 INVITE / 100 Trying\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "4. Rx              SIP/2.0 / 102 INVITE / 200 OK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "5. TxReq           ACK / 102 ACK - ACK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "6. Rx              BYE / 102 BYE / sip:ast2@127.0.0.2:5060\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "7. RTCPaudio       Quality:ssrc=28249381;themssrc=485141946;lp=0;rxjitter=0.000029\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "8. RTCPaudioJitter Quality:minrxjitter=0.000000;maxrxjitter=0.000000;avgrxjitter=0\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "9. RTCPaudioLoss   Quality:minrxlost=0.000000;maxrxlost=0.000000;avgrxlost=0.00000\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "10. RTCPaudioRTT    Quality:minrtt=0.000000;maxrtt=0.000000;avgrtt=0.000000;stdevrt\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "11. SchedDestroy    32000 ms\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "12. TxResp          SIP/2.0 / 102 BYE - 200 OK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "13. Hangup          Cause Normal Clearing\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        elif command == "sip show history 2ec048aa4ed1239664f6408f0c5044c5@127.0.0.2:5060":</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string = "* SIP Call\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "1. NewChan         Channel SIP/ast1-00000002 - from 2ec048aa4ed1239664f6408f0c5044\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "2. TxReqRel        INVITE / 102 INVITE - INVITE\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "3. Rx              SIP/2.0 / 102 INVITE / 100 Trying\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "4. Rx              SIP/2.0 / 102 INVITE / 200 OK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "5. TxReq           ACK / 102 ACK - ACK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "6. Rx              BYE / 102 BYE / sip:ast2@127.0.0.2:5060\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "7. RTCPaudio       Quality:ssrc=28249381;themssrc=485141946;lp=0;rxjitter=0.000029\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "8. RTCPaudioJitter Quality:minrxjitter=0.000000;maxrxjitter=0.000000;avgrxjitter=0\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "9. RTCPaudioLoss   Quality:minrxlost=0.000000;maxrxlost=0.000000;avgrxlost=0.00000\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "10. RTCPaudioRTT    Quality:minrtt=0.000000;maxrtt=0.000000;avgrtt=0.000000;stdevrt\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "11. SchedDestroy    32000 ms\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "12. TxResp          SIP/2.0 / 102 BYE - 200 OK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "13. Hangup          Cause Normal Clearing\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        return ret_string</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class AstMockObjectPreTestFail(object):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Mock out CLI execution from Asterisk instance</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    This mock object provides history during a pre-test call, which is wrong</span><br><span style="color: hsl(0, 100%, 40%);">-    """</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Constructor"""</span><br><span style="color: hsl(0, 100%, 40%);">-        self.host = "127.0.0.2"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def cli_exec(self, command):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Mock CLI execution/response"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        ret_string = ""</span><br><span style="color: hsl(0, 100%, 40%);">-        if command == "sip show objects":</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string = "-= Peer objects: 4 static, 0 realtime, 0 autocreate =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7001\ntype: peer\nobjflags: 0\nrefcount: 1\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Peer objects by IP =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Registry objects: 0 =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Dialog objects:\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 2ec048aa4ed1239664f6408f0c5044c4@127.0.0.2:5060\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "type: dialog\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "objflags: 0\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "refcount: 2\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        elif command == "sip show history 2ec048aa4ed1239664f6408f0c5044c4@127.0.0.2:5060":</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string = "* SIP Call\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "1. NewChan         Channel SIP/ast1-00000002 - from 2ec048aa4ed1239664f6408f0c5044\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "2. TxReqRel        INVITE / 102 INVITE - INVITE\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "3. Rx              SIP/2.0 / 102 INVITE / 100 Trying\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "4. Rx              SIP/2.0 / 102 INVITE / 200 OK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "5. TxReq           ACK / 102 ACK - ACK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "6. Rx              BYE / 102 BYE / sip:ast2@127.0.0.2:5060\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "7. RTCPaudio       Quality:ssrc=28249381;themssrc=485141946;lp=0;rxjitter=0.000029\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "8. RTCPaudioJitter Quality:minrxjitter=0.000000;maxrxjitter=0.000000;avgrxjitter=0\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "9. RTCPaudioLoss   Quality:minrxlost=0.000000;maxrxlost=0.000000;avgrxlost=0.00000\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "10. RTCPaudioRTT    Quality:minrtt=0.000000;maxrtt=0.000000;avgrtt=0.000000;stdevrt\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "11. SchedDestroy    32000 ms\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "12. TxResp          SIP/2.0 / 102 BYE - 200 OK\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "13. Hangup          Cause Normal Clearing\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        return ret_string</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class AstMockObjectPreTestPass(object):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Mock out CLI execution from Asterisk instance</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    This mock object provides no history during a pre-test call, which is the</span><br><span style="color: hsl(0, 100%, 40%);">-    expected state</span><br><span style="color: hsl(0, 100%, 40%);">-    """</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Constructor"""</span><br><span style="color: hsl(0, 100%, 40%);">-        self.host = "127.0.0.1"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def cli_exec(self, command):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Mock CLI execution/response"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        ret_string = ""</span><br><span style="color: hsl(0, 100%, 40%);">-        if command == "sip show objects":</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string = "-= Peer objects: 4 static, 0 realtime, 0 autocreate =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7001\ntype: peer\nobjflags: 0\nrefcount: 1\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Peer objects by IP =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: zoiper_01\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: audio\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "name: 7002\ntype: peer\nobjflags: 0\nrefcount: 3\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Registry objects: 0 =-\n\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            ret_string += "-= Dialog objects:\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        elif command == "sip show history":</span><br><span style="color: hsl(0, 100%, 40%);">-            return "\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        return ret_string</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class SipDialogTestConditionUnitTest(unittest.TestCase):</span><br><span style="color: hsl(0, 100%, 40%);">-    """Unit tests for SipDialogTestCondition objects"""</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_pre_test_pass(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Verify that acceptable pre-test output passes"""</span><br><span style="color: hsl(0, 100%, 40%);">-        ast = AstMockObjectPreTestPass()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = SipDialogPreTestCondition(TestConfig())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(ast)</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Passed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_pre_test_fail(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Verify that unacceptable pre-test output fails"""</span><br><span style="color: hsl(0, 100%, 40%);">-        ast = AstMockObjectPreTestFail()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = SipDialogPreTestCondition(TestConfig())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(ast)</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_pre_test_fail_multi_asterisk(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Verify that pre-test output from multiple sources fails when one</span><br><span style="color: hsl(0, 100%, 40%);">-        of those sources is bad"""</span><br><span style="color: hsl(0, 100%, 40%);">-        ast1 = AstMockObjectPreTestFail()</span><br><span style="color: hsl(0, 100%, 40%);">-        ast2 = AstMockObjectPreTestPass()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = SipDialogPreTestCondition(TestConfig())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(ast1)</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(ast2)</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_post_test_pass(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Verify nominal post-test output"""</span><br><span style="color: hsl(0, 100%, 40%);">-        ast = AstMockObjectPostTestPass()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = SipDialogPostTestCondition(TestConfigWithHistory())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(ast)</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Passed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_post_test_no_dialog_pass(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Verify nominal post-test output with no dialogs passes"""</span><br><span style="color: hsl(0, 100%, 40%);">-        ast = AstMockObjectPostTestNoDialogsPass()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = SipDialogPostTestCondition(TestConfig())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(ast)</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Passed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_post_test_no_hangup_fail(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Verify no hangup detection is caught and results in a failure"""</span><br><span style="color: hsl(0, 100%, 40%);">-        ast = AstMockObjectPostTestNoHangupFail()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = SipDialogPostTestCondition(TestConfigWithHistory())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(ast)</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_post_test_no_destruction_fail(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Verify no destruction is caught and results in a failure"""</span><br><span style="color: hsl(0, 100%, 40%);">-        ast = AstMockObjectPostTestNoDestructionFail()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = SipDialogPostTestCondition(TestConfigWithHistory())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(ast)</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_post_test_multi_asterisk_fail(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Test multiple instances of Asterisk where a single failure causes</span><br><span style="color: hsl(0, 100%, 40%);">-        a failure in the overall result"""</span><br><span style="color: hsl(0, 100%, 40%);">-        ast1 = AstMockObjectPostTestNoHangupFail()</span><br><span style="color: hsl(0, 100%, 40%);">-        ast2 = AstMockObjectPostTestNoDestructionFail()</span><br><span style="color: hsl(0, 100%, 40%);">-        ast3 = AstMockObjectPostTestNoDialogsPass()</span><br><span style="color: hsl(0, 100%, 40%);">-        ast4 = AstMockObjectPostTestPass()</span><br><span style="color: hsl(0, 100%, 40%);">-        obj = SipDialogPostTestCondition(TestConfigWithHistory())</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(ast1)</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(ast2)</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(ast3)</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.register_asterisk_instance(ast4)</span><br><span style="color: hsl(0, 100%, 40%);">-        obj.evaluate()</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(obj.get_status(), 'Failed')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-def main():</span><br><span style="color: hsl(0, 100%, 40%);">-    """Execute the unit tests"""</span><br><span style="color: hsl(0, 100%, 40%);">-    logging.basicConfig(level=logging.DEBUG)</span><br><span style="color: hsl(0, 100%, 40%);">-    unittest.main()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-if __name__ == "__main__":</span><br><span style="color: hsl(0, 100%, 40%);">-    main()</span><br><span>diff --git a/lib/python/asterisk/sipp.py b/lib/python/asterisk/sipp.py</span><br><span>index b84965e..540a544 100644</span><br><span>--- a/lib/python/asterisk/sipp.py</span><br><span>+++ b/lib/python/asterisk/sipp.py</span><br><span>@@ -10,12 +10,12 @@</span><br><span> """</span><br><span> </span><br><span> import logging</span><br><span style="color: hsl(0, 100%, 40%);">-import test_suite_utils</span><br><span style="color: hsl(120, 100%, 40%);">+from . import test_suite_utils</span><br><span> </span><br><span> from abc import ABCMeta, abstractmethod</span><br><span> from twisted.internet import reactor, defer, protocol, error</span><br><span style="color: hsl(0, 100%, 40%);">-from test_case import TestCase</span><br><span style="color: hsl(0, 100%, 40%);">-from utils_socket import get_available_port</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_case import TestCase</span><br><span style="color: hsl(120, 100%, 40%);">+from .utils_socket import get_available_port</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>@@ -495,7 +495,7 @@</span><br><span>     def outReceived(self, data):</span><br><span>         """Override of ProcessProtocol.outReceived"""</span><br><span>         LOGGER.debug("Received from SIPp scenario %s: %s" % (self._name, data))</span><br><span style="color: hsl(0, 100%, 40%);">-        self.output += data</span><br><span style="color: hsl(120, 100%, 40%);">+        self.output += data.decode('utf-8', 'ignore')</span><br><span> </span><br><span>     def connectionMade(self):</span><br><span>         """Override of ProcessProtocol.connectionMade"""</span><br><span>diff --git a/lib/python/asterisk/sippversion.py b/lib/python/asterisk/sippversion.py</span><br><span>index b1110fc..f942e9c 100644</span><br><span>--- a/lib/python/asterisk/sippversion.py</span><br><span>+++ b/lib/python/asterisk/sippversion.py</span><br><span>@@ -1,4 +1,3 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python</span><br><span> """SIPp Version String Handling</span><br><span> </span><br><span> Copyright (C) 2010, Digium, Inc.</span><br><span>@@ -9,11 +8,7 @@</span><br><span> """</span><br><span> </span><br><span> import subprocess</span><br><span style="color: hsl(0, 100%, 40%);">-import sys</span><br><span style="color: hsl(0, 100%, 40%);">-import unittest</span><br><span style="color: hsl(0, 100%, 40%);">-sys.path.append("lib/python")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-import test_suite_utils</span><br><span style="color: hsl(120, 100%, 40%);">+from . import test_suite_utils</span><br><span> </span><br><span> </span><br><span> class SIPpVersion:</span><br><span>@@ -48,8 +43,9 @@</span><br><span>             except OSError:</span><br><span>                 return</span><br><span>             for line in sipp_process.stdout:</span><br><span style="color: hsl(0, 100%, 40%);">-                if line.strip().startswith('SIPp '):</span><br><span style="color: hsl(0, 100%, 40%);">-                    sipp = line.strip()[5:]</span><br><span style="color: hsl(120, 100%, 40%);">+                line = line.decode('utf-8').strip()</span><br><span style="color: hsl(120, 100%, 40%);">+                if line.startswith('SIPp '):</span><br><span style="color: hsl(120, 100%, 40%);">+                    sipp = line[5:]</span><br><span>                     sipp = sipp.split(',', 1)</span><br><span>                     sipp = sipp[0].split('-', 1)</span><br><span>                     version = sipp[0]</span><br><span>@@ -88,7 +84,7 @@</span><br><span> </span><br><span>     def __cmp__(self, other):</span><br><span>         """Compare two SIPpVersion instances against each other"""</span><br><span style="color: hsl(0, 100%, 40%);">-        return cmp(int(self), int(other))</span><br><span style="color: hsl(120, 100%, 40%);">+        return (int(self) > int(other)) - (int(self) < int(other))</span><br><span> </span><br><span>     def __ne__(self, other):</span><br><span>         """Determine if this SIPpVersion instance is not equal to another"""</span><br><span>@@ -106,6 +102,14 @@</span><br><span>                 return True</span><br><span>         return False</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+    def __le__(self, other):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Determine if this SIPpVersion instance is less than or equal to another"""</span><br><span style="color: hsl(120, 100%, 40%);">+        return int(self) <= int(other)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __lt__(self, other):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Determine if this SIPpVersion instance is less than another"""</span><br><span style="color: hsl(120, 100%, 40%);">+        return int(self) < int(other)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>     def __parse_version(self, version):</span><br><span>         """Parse the version string returned from SIPp"""</span><br><span>         self.version_str = version</span><br><span>@@ -124,215 +128,3 @@</span><br><span>             self.tls = True</span><br><span>         if value.find("PCAP") > -1:</span><br><span>             self.pcap = True</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class SIPpVersionTests(unittest.TestCase):</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_version(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v = SIPpVersion("v3.2", None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(str(v), "v3.2")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.concept, "v3")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.major, "2")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.minor, None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(v.tls)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(v.pcap)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_version2(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v = SIPpVersion("v2.0.1", None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(str(v), "v2.0.1")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.concept, "v2")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.major, "0")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.minor, "1")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(v.tls)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(v.pcap)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_version3(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v = SIPpVersion("v3.1", "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(str(v), "v3.1-TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.concept, "v3")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.major, "1")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.minor, None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v.tls)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(v.pcap)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_version4(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v = SIPpVersion("v2.0.1", "TLS-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(str(v), "v2.0.1-TLS-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.concept, "v2")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.major, "0")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.minor, "1")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v.tls)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v.pcap)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_version5(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v = SIPpVersion("v3.2", "PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(str(v), "v3.2-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.concept, "v3")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.major, "2")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.minor, None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(v.tls)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v.pcap)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_version6(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v = SIPpVersion(None, "PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(str(v), "PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.concept, None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.major, None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.minor, None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(v.tls)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v.pcap)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_version7(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v = SIPpVersion(None, "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(str(v), "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.concept, None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.major, None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.minor, None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v.tls)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(v.pcap)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_version8(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v = SIPpVersion(None, "TLS-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(str(v), "TLS-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.concept, None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.major, None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertEqual(v.minor, None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v.tls)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v.pcap)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.2", None)</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.1", None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 > v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp2(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v2.0.1", None)</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.1", None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 < v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp3(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.1", None)</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.1", None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 == v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp4(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.1", None)</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.1", None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(v1 != v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp5(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.1", "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.1", "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 == v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp6(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion(None, "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion(None, "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 == v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp7(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v2.0.1", None)</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v2.0.1", None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 == v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp8(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.2", "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.2", "PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 != v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp9(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion(None, "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion(None, "PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 != v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp10(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.2", "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.2", "PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(v1 == v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp11(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion(None, "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion(None, "PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(v1 == v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp12(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.2", "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 >= v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp13(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion(None, "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 >= v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp14(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.2", "PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 >= v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp15(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion(None, "PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 >= v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp16(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.2", "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 != v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp17(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion(None, "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 != v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp18(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.2", "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(v1 == v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp19(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion(None, "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(v1 == v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp20(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.1", "PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.0", "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 > v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp21(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.2", "TLS-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v2.0.1", "PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 >= v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp22(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.1", "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.1", "PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(v1 > v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp23(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.1", "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.1", "PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(v1 < v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp24(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.2", "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.1", "TLS-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 >= v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp25(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v3.1", "TLS-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.2", "PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertTrue(v1 <= v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_cmp26(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        v1 = SIPpVersion("v2.0.1", "TLS-PCAP")</span><br><span style="color: hsl(0, 100%, 40%);">-        v2 = SIPpVersion("v3.0", "TLS")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.assertFalse(v1 >= v2)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-def main():</span><br><span style="color: hsl(0, 100%, 40%);">-    unittest.main()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-if __name__ == "__main__":</span><br><span style="color: hsl(0, 100%, 40%);">-    main()</span><br><span>diff --git a/lib/python/asterisk/syncami.py b/lib/python/asterisk/syncami.py</span><br><span>index 031ae99..5482400 100644</span><br><span>--- a/lib/python/asterisk/syncami.py</span><br><span>+++ b/lib/python/asterisk/syncami.py</span><br><span>@@ -8,7 +8,13 @@</span><br><span> the GNU General Public License Version 2.</span><br><span> """</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from urllib import urlencode</span><br><span style="color: hsl(120, 100%, 40%);">+try:</span><br><span style="color: hsl(120, 100%, 40%);">+    # python 2 import</span><br><span style="color: hsl(120, 100%, 40%);">+    from urllib import urlencode</span><br><span style="color: hsl(120, 100%, 40%);">+except:</span><br><span style="color: hsl(120, 100%, 40%);">+    # python 3 import</span><br><span style="color: hsl(120, 100%, 40%);">+    from urllib.parse import urlencode</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> from email.parser import HeaderParser</span><br><span> try:</span><br><span>     from httplib import *</span><br><span>@@ -87,7 +93,7 @@</span><br><span>         if res.status != 200:</span><br><span>             raise InvalidAMIResponse(res)</span><br><span>         self.cookie = res.getheader('set-cookie', None)</span><br><span style="color: hsl(0, 100%, 40%);">-        data = res.read()</span><br><span style="color: hsl(120, 100%, 40%);">+        data = res.read().decode('utf-8')</span><br><span>         parser = HeaderParser()</span><br><span> </span><br><span>         return parser.parsestr(data)</span><br><span>diff --git a/lib/python/asterisk/test_case.py b/lib/python/asterisk/test_case.py</span><br><span>index 80321f7..7bf6d16 100644</span><br><span>--- a/lib/python/asterisk/test_case.py</span><br><span>+++ b/lib/python/asterisk/test_case.py</span><br><span>@@ -19,9 +19,9 @@</span><br><span> from twisted.python import log</span><br><span> from starpy import manager, fastagi</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from asterisk import Asterisk</span><br><span style="color: hsl(0, 100%, 40%);">-from test_config import TestConfig</span><br><span style="color: hsl(0, 100%, 40%);">-from test_conditions import TestConditionController</span><br><span style="color: hsl(120, 100%, 40%);">+from .asterisk import Asterisk</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_config import TestConfig</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_conditions import TestConditionController</span><br><span> </span><br><span> </span><br><span> try:</span><br><span>@@ -45,12 +45,12 @@</span><br><span>         except:</span><br><span>             msg = ("WARNING: failed to preserve existing loggers - some "</span><br><span>                    "logging statements may be missing")</span><br><span style="color: hsl(0, 100%, 40%);">-            print msg</span><br><span style="color: hsl(120, 100%, 40%);">+            print(msg)</span><br><span>             logging.config.fileConfig(config_file)</span><br><span>     else:</span><br><span>         msg = ("WARNING: no logging.conf file found; using default "</span><br><span>                "configuration")</span><br><span style="color: hsl(0, 100%, 40%);">-        print msg</span><br><span style="color: hsl(120, 100%, 40%);">+        print(msg)</span><br><span>         logging.basicConfig(level=logging.DEBUG)</span><br><span> </span><br><span>     root_logger = logging.getLogger()</span><br><span>@@ -102,7 +102,7 @@</span><br><span>         # for the rasterisk CLI connection. As a quick fix, we hash the path</span><br><span>         # using md5, to make it unique enough.</span><br><span>         self.realbase = self.test_name.replace("tests/", "", 1)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.base = md5(self.realbase).hexdigest()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.base = md5(self.realbase.encode()).hexdigest()</span><br><span>         # We provide a symlink to it from a named path.</span><br><span>         named_dir = os.path.join(Asterisk.test_suite_root, self.realbase)</span><br><span>         try:</span><br><span>diff --git a/lib/python/asterisk/test_conditions.py b/lib/python/asterisk/test_conditions.py</span><br><span>index dbf36a7..7053465 100644</span><br><span>--- a/lib/python/asterisk/test_conditions.py</span><br><span>+++ b/lib/python/asterisk/test_conditions.py</span><br><span>@@ -19,7 +19,7 @@</span><br><span> import logging</span><br><span> import logging.config</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from buildoptions import AsteriskBuildOptions</span><br><span style="color: hsl(120, 100%, 40%);">+from .buildoptions import AsteriskBuildOptions</span><br><span> from twisted.internet import defer</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span>diff --git a/lib/python/asterisk/test_config.py b/lib/python/asterisk/test_config.py</span><br><span>index 8fdb884..6d2318d 100644</span><br><span>--- a/lib/python/asterisk/test_config.py</span><br><span>+++ b/lib/python/asterisk/test_config.py</span><br><span>@@ -18,12 +18,13 @@</span><br><span> </span><br><span> sys.path.append("lib/python")</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-import test_suite_utils</span><br><span style="color: hsl(120, 100%, 40%);">+from . import test_suite_utils</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_runner import load_and_parse_module</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from asterisk import Asterisk</span><br><span style="color: hsl(0, 100%, 40%);">-from buildoptions import AsteriskBuildOptions</span><br><span style="color: hsl(0, 100%, 40%);">-from sippversion import SIPpVersion</span><br><span style="color: hsl(0, 100%, 40%);">-from opensslversion import OpenSSLVersion</span><br><span style="color: hsl(120, 100%, 40%);">+from .asterisk import Asterisk</span><br><span style="color: hsl(120, 100%, 40%);">+from .buildoptions import AsteriskBuildOptions</span><br><span style="color: hsl(120, 100%, 40%);">+from .sippversion import SIPpVersion</span><br><span style="color: hsl(120, 100%, 40%);">+from .opensslversion import OpenSSLVersion</span><br><span> </span><br><span> </span><br><span> class TestConditionConfig(object):</span><br><span>@@ -70,14 +71,9 @@</span><br><span> </span><br><span>     def make_condition(self):</span><br><span>         """Build and return the condition object defined by this config"""</span><br><span style="color: hsl(0, 100%, 40%);">-        parts = self.class_type_name.split('.')</span><br><span style="color: hsl(0, 100%, 40%);">-        module = '.'.join(parts[:-1])</span><br><span style="color: hsl(0, 100%, 40%);">-        if module != '':</span><br><span style="color: hsl(0, 100%, 40%);">-            mod = __import__(module)</span><br><span style="color: hsl(0, 100%, 40%);">-            for comp in parts[1:]:</span><br><span style="color: hsl(0, 100%, 40%);">-                mod = getattr(mod, comp)</span><br><span style="color: hsl(0, 100%, 40%);">-            obj = mod(self)</span><br><span style="color: hsl(0, 100%, 40%);">-            return obj</span><br><span style="color: hsl(120, 100%, 40%);">+        mod = load_and_parse_module(self.class_type_name)</span><br><span style="color: hsl(120, 100%, 40%);">+        if mod is not None:</span><br><span style="color: hsl(120, 100%, 40%);">+            return mod(self)</span><br><span>         return None</span><br><span> </span><br><span> </span><br><span>@@ -147,7 +143,7 @@</span><br><span>                     self.met = getattr(self, dir_method)()</span><br><span>                     found = True</span><br><span>             if not found:</span><br><span style="color: hsl(0, 100%, 40%);">-                print "Unknown custom dependency - '%s'" % self.name</span><br><span style="color: hsl(120, 100%, 40%);">+                print("Unknown custom dependency - '%s'" % self.name)</span><br><span>         elif "asterisk" in dep:</span><br><span>             if self.ast:</span><br><span>                 self.name = dep["asterisk"]</span><br><span>@@ -167,9 +163,9 @@</span><br><span>             from test_case import PCAP_AVAILABLE</span><br><span>             self.met = PCAP_AVAILABLE</span><br><span>         else:</span><br><span style="color: hsl(0, 100%, 40%);">-            print "Unknown dependency type specified:"</span><br><span style="color: hsl(120, 100%, 40%);">+            print("Unknown dependency type specified:")</span><br><span>             for key in dep.keys():</span><br><span style="color: hsl(0, 100%, 40%);">-                print key</span><br><span style="color: hsl(120, 100%, 40%);">+                print(key)</span><br><span> </span><br><span>     def depend_remote(self):</span><br><span>         """Check to see if we run against a remote instance of Asterisk"""</span><br><span>@@ -256,13 +252,13 @@</span><br><span>         if self.asterisk_build_options:</span><br><span>             return (self.asterisk_build_options.check_option(name))</span><br><span>         else:</span><br><span style="color: hsl(0, 100%, 40%);">-            print "Unable to evaluate build options: no build options found"</span><br><span style="color: hsl(120, 100%, 40%);">+            print("Unable to evaluate build options: no build options found")</span><br><span>             return False</span><br><span> </span><br><span>     def _find_asterisk_module(self, name):</span><br><span>         """Determine if an Asterisk module exists"""</span><br><span>         if not Dependency.ast:</span><br><span style="color: hsl(0, 100%, 40%);">-            print "Unable to evaluate Asterisk modules: Asterisk not found"</span><br><span style="color: hsl(120, 100%, 40%);">+            print("Unable to evaluate Asterisk modules: Asterisk not found")</span><br><span>             return False</span><br><span> </span><br><span>         if Dependency.ast.original_astmoddir == "":</span><br><span>@@ -333,8 +329,8 @@</span><br><span>                 if self.config is not None and 'exclude-tests' in self.config:</span><br><span>                     self.excluded_tests = self.config['exclude-tests']</span><br><span>             else:</span><br><span style="color: hsl(0, 100%, 40%);">-                print ("WARNING - test configuration [%s] not found in "</span><br><span style="color: hsl(0, 100%, 40%);">-                       "config file" % self.test_configuration)</span><br><span style="color: hsl(120, 100%, 40%);">+                print("WARNING - test configuration [%s] not found in "</span><br><span style="color: hsl(120, 100%, 40%);">+                      "config file" % self.test_configuration)</span><br><span> </span><br><span>     def _process_testinfo(self):</span><br><span>         """Process the test information block"""</span><br><span>@@ -381,8 +377,8 @@</span><br><span>             self.config = yaml.load(config_file)</span><br><span> </span><br><span>         if not self.config:</span><br><span style="color: hsl(0, 100%, 40%);">-            print "ERROR: Failed to load configuration for test '%s'" % \</span><br><span style="color: hsl(0, 100%, 40%);">-                self.test_name</span><br><span style="color: hsl(120, 100%, 40%);">+            print("ERROR: Failed to load configuration for test '%s'" %</span><br><span style="color: hsl(120, 100%, 40%);">+                  self.test_name)</span><br><span>             return</span><br><span> </span><br><span>         self._process_global_settings()</span><br><span>@@ -408,8 +404,8 @@</span><br><span>             matches = [cond_def for cond_def in self.condition_definitions</span><br><span>                        if cond_def['name'] == conf['name']]</span><br><span>             if len(matches) != 1:</span><br><span style="color: hsl(0, 100%, 40%);">-                print ("Unknown or too many matches for condition: " +</span><br><span style="color: hsl(0, 100%, 40%);">-                       conf['name'])</span><br><span style="color: hsl(120, 100%, 40%);">+                print("Unknown or too many matches for condition: " +</span><br><span style="color: hsl(120, 100%, 40%);">+                      conf['name'])</span><br><span>             else:</span><br><span>                 pre_cond = TestConditionConfig(conf, matches[0], "Pre")</span><br><span>                 post_cond = TestConditionConfig(conf, matches[0], "Post")</span><br><span>diff --git a/lib/python/asterisk/test_runner.py b/lib/python/asterisk/test_runner.py</span><br><span>old mode 100755</span><br><span>new mode 100644</span><br><span>index 644d117..d278e02</span><br><span>--- a/lib/python/asterisk/test_runner.py</span><br><span>+++ b/lib/python/asterisk/test_runner.py</span><br><span>@@ -1,4 +1,3 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python</span><br><span> """Module that spawns and manages running a test</span><br><span> </span><br><span> This module provides an entry point, loading, and teardown of test</span><br><span>@@ -23,8 +22,6 @@</span><br><span> LOGGER = logging.getLogger('test_runner')</span><br><span> logging.basicConfig()</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-sys.path.append('lib/python')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> </span><br><span> class TestModuleFinder(object):</span><br><span>     """Determines if a module is a test module that can be loaded"""</span><br><span>@@ -144,9 +141,18 @@</span><br><span>     module_name = ".".join(parts[:-1])</span><br><span> </span><br><span>     if not len(module_name):</span><br><span style="color: hsl(0, 100%, 40%);">-        LOGGER.error("No module specified: %s" % module_name)</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGGER.error("No module specified: %s" % typename)</span><br><span>         return None</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+    if os.path.exists('lib/python/asterisk/%s.py' % module_name):</span><br><span style="color: hsl(120, 100%, 40%);">+        # This is convoluted but required.  lib/python/asterisk packages</span><br><span style="color: hsl(120, 100%, 40%);">+        # must be loaded using absolute package names and 'asterisk' must</span><br><span style="color: hsl(120, 100%, 40%);">+        # be included in the list of parts.  We cannot simply prepend</span><br><span style="color: hsl(120, 100%, 40%);">+        # type_name from the start because this blocks load of modules</span><br><span style="color: hsl(120, 100%, 40%);">+        # that are local to the test (add-test-to-search-path: 'True').</span><br><span style="color: hsl(120, 100%, 40%);">+        module_name = 'asterisk.' + module_name</span><br><span style="color: hsl(120, 100%, 40%);">+        parts = ['asterisk'] + parts</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>     module = __import__(module_name)</span><br><span>     for comp in parts[1:]:</span><br><span>         module = getattr(module, comp)</span><br><span>diff --git a/lib/python/asterisk/test_suite_utils.py b/lib/python/asterisk/test_suite_utils.py</span><br><span>index d8ef541..84f54f5 100644</span><br><span>--- a/lib/python/asterisk/test_suite_utils.py</span><br><span>+++ b/lib/python/asterisk/test_suite_utils.py</span><br><span>@@ -1,4 +1,3 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#! /usr/bin/env python</span><br><span> """Asterisk testsuite utils</span><br><span> </span><br><span> This module provides access to Asterisk testsuite utility</span><br><span>@@ -14,16 +13,20 @@</span><br><span> import os</span><br><span> import logging</span><br><span> import re</span><br><span style="color: hsl(120, 100%, 40%);">+import sys</span><br><span> </span><br><span> from os import close</span><br><span> from os import remove</span><br><span> from shutil import move</span><br><span> from tempfile import mkstemp</span><br><span style="color: hsl(0, 100%, 40%);">-from config import ConfigFile</span><br><span style="color: hsl(120, 100%, 40%);">+from .config import ConfigFile</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+if sys.version_info[0] == 3:</span><br><span style="color: hsl(120, 100%, 40%);">+    unicode = str</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def which(program):</span><br><span>     """Find the executable for a specified program</span><br><span> </span><br><span>@@ -100,7 +103,7 @@</span><br><span>     elif isinstance(pattern, dict):</span><br><span>         # Dict should match for every field in the pattern.</span><br><span>         # extra fields in the message are fine.</span><br><span style="color: hsl(0, 100%, 40%);">-        for key, value in pattern.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+        for key, value in pattern.items():</span><br><span>             to_check = message.get(key)</span><br><span>             if to_check is None or not all_match(value, to_check):</span><br><span>                 return False</span><br><span>diff --git a/lib/python/asterisk/thread_test_condition.py b/lib/python/asterisk/thread_test_condition.py</span><br><span>index 595e3ce..c12321f 100644</span><br><span>--- a/lib/python/asterisk/thread_test_condition.py</span><br><span>+++ b/lib/python/asterisk/thread_test_condition.py</span><br><span>@@ -9,7 +9,7 @@</span><br><span> """</span><br><span> </span><br><span> import logging</span><br><span style="color: hsl(0, 100%, 40%);">-from test_conditions import TestCondition</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_conditions import TestCondition</span><br><span> from twisted.internet import defer</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span>diff --git a/lib/python/asterisk/voicemail.py b/lib/python/asterisk/voicemail.py</span><br><span>index 283650b..3c4c7d5 100644</span><br><span>--- a/lib/python/asterisk/voicemail.py</span><br><span>+++ b/lib/python/asterisk/voicemail.py</span><br><span>@@ -20,9 +20,9 @@</span><br><span> import time</span><br><span> import random</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from config import ConfigFile</span><br><span style="color: hsl(0, 100%, 40%);">-from test_case import TestCase</span><br><span style="color: hsl(0, 100%, 40%);">-from test_state import TestState, TestStateController, FailureTestState</span><br><span style="color: hsl(120, 100%, 40%);">+from .config import ConfigFile</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_case import TestCase</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_state import TestState, TestStateController, FailureTestState</span><br><span> </span><br><span> sys.path.append("lib/python")</span><br><span> </span><br><span>diff --git a/lib/python/rlmi.py b/lib/python/rlmi.py</span><br><span>index 5e662a0..2abf19e 100644</span><br><span>--- a/lib/python/rlmi.py</span><br><span>+++ b/lib/python/rlmi.py</span><br><span>@@ -343,7 +343,7 @@</span><br><span>             return None</span><br><span>         @classmethod</span><br><span>         def gds_reverse_node_mapping(cls, mapping):</span><br><span style="color: hsl(0, 100%, 40%);">-            return dict(((v, k) for k, v in mapping.iteritems()))</span><br><span style="color: hsl(120, 100%, 40%);">+            return dict(((v, k) for k, v in mapping.items()))</span><br><span> </span><br><span> </span><br><span> #</span><br><span>diff --git a/lib/python/sip_message.py b/lib/python/sip_message.py</span><br><span>index 85cd9fd..087c760 100644</span><br><span>--- a/lib/python/sip_message.py</span><br><span>+++ b/lib/python/sip_message.py</span><br><span>@@ -118,7 +118,7 @@</span><br><span> def main():</span><br><span>     msg = """INVITE sip:123@example.com SIP/2.0\r\nContact   : \tTerry Wilson\r\n   <terry@example.com>\r\nCall-ID:\r\n Whatever\r\nContact: New Contact\r\n\r\nData!!!!!"""</span><br><span>     sipmsg = SIPMessage(msg)</span><br><span style="color: hsl(0, 100%, 40%);">-    print sipmsg</span><br><span style="color: hsl(120, 100%, 40%);">+    print(sipmsg)</span><br><span>     if sipmsg.get_header('CoNtact') is None:</span><br><span>         return -1</span><br><span>     if len(sipmsg.get_header_all('contact')) != 2:</span><br><span>diff --git a/run-local b/run-local</span><br><span>index 026a063..326ac50 100755</span><br><span>--- a/run-local</span><br><span>+++ b/run-local</span><br><span>@@ -75,7 +75,12 @@</span><br><span>        LIBDIR=`dirname $HERE/astroot/usr/lib*/libasteriskssl.so`</span><br><span>    export LD_LIBRARY_PATH="${LIBDIR}${LD_LIBRARY_PATH}"</span><br><span>       set +e</span><br><span style="color: hsl(0, 100%, 40%);">-  ./runtests.py "$@"</span><br><span style="color: hsl(120, 100%, 40%);">+  if test -n "$PYTHON"; then</span><br><span style="color: hsl(120, 100%, 40%);">+          # Use specific interpreter</span><br><span style="color: hsl(120, 100%, 40%);">+            $PYTHON ./runtests.py "$@"</span><br><span style="color: hsl(120, 100%, 40%);">+  else</span><br><span style="color: hsl(120, 100%, 40%);">+          ./runtests.py "$@"</span><br><span style="color: hsl(120, 100%, 40%);">+  fi</span><br><span>   status=$?</span><br><span>    rm "$AST_TEST_ROOT"</span><br><span>        set -e</span><br><span>diff --git a/runtests.py b/runtests.py</span><br><span>index 1351a3c..eac88a1 100755</span><br><span>--- a/runtests.py</span><br><span>+++ b/runtests.py</span><br><span>@@ -111,7 +111,7 @@</span><br><span> </span><br><span>     def stdout_print(self, msg):</span><br><span>         self.stdout += msg + "\n"</span><br><span style="color: hsl(0, 100%, 40%);">-        print msg</span><br><span style="color: hsl(120, 100%, 40%);">+        print(msg)</span><br><span> </span><br><span>     def run(self):</span><br><span>         self.passed = False</span><br><span>@@ -124,13 +124,14 @@</span><br><span>         ]</span><br><span> </span><br><span>         if not os.path.exists(cmd[0]):</span><br><span style="color: hsl(0, 100%, 40%);">-            cmd = ["./lib/python/asterisk/test_runner.py",</span><br><span style="color: hsl(120, 100%, 40%);">+            cmd = [sys.executable,</span><br><span style="color: hsl(120, 100%, 40%);">+                   "-m", "asterisk.test_runner",</span><br><span>                    "%s" % self.test_name]</span><br><span>         if os.path.exists(cmd[0]) and os.access(cmd[0], os.X_OK):</span><br><span>             if self.options.pcap:</span><br><span>                 os.environ['PCAP'] = "yes"</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-            self.stdout_print("Running %s ..." % cmd)</span><br><span style="color: hsl(120, 100%, 40%);">+            self.stdout_print("Running %s ..." % self.test_name)</span><br><span>             p = subprocess.Popen(cmd, stdout=subprocess.PIPE,</span><br><span>                                  stderr=subprocess.STDOUT)</span><br><span>             self.pid = p.pid</span><br><span>@@ -139,6 +140,7 @@</span><br><span>             poll.register(p.stdout, select.POLLIN)</span><br><span> </span><br><span>             timedout = False</span><br><span style="color: hsl(120, 100%, 40%);">+            has_unicode_error = False</span><br><span>             try:</span><br><span>                 while (not abandon_test):</span><br><span>                     try:</span><br><span>@@ -148,20 +150,24 @@</span><br><span>                     except select.error as v:</span><br><span>                         if v[0] != errno.EINTR:</span><br><span>                             raise</span><br><span style="color: hsl(0, 100%, 40%);">-                    l = p.stdout.readline()</span><br><span style="color: hsl(120, 100%, 40%);">+                    l = p.stdout.readline().decode('ascii', 'ignore').strip()</span><br><span>                     if not l:</span><br><span>                         break</span><br><span>                     self.stdout_print(l)</span><br><span style="color: hsl(120, 100%, 40%);">+            except UnicodeEncodeError:</span><br><span style="color: hsl(120, 100%, 40%);">+                self.stdout_print('Unicode error reading output from test!')</span><br><span style="color: hsl(120, 100%, 40%);">+                has_unicode_error = True</span><br><span style="color: hsl(120, 100%, 40%);">+                pass</span><br><span>             except IOError:</span><br><span>                 pass</span><br><span>             p.wait()</span><br><span> </span><br><span>             # Sanitize p.returncode so it's always a boolean.</span><br><span style="color: hsl(0, 100%, 40%);">-            did_pass = (p.returncode == 0 and not abandon_test)</span><br><span style="color: hsl(120, 100%, 40%);">+            did_pass = (p.returncode == 0 and not abandon_test and not has_unicode_error)</span><br><span>             if did_pass and not self.test_config.expect_pass:</span><br><span>                 self.stdout_print("Test passed but was expected to fail.")</span><br><span>             if not did_pass and not self.test_config.expect_pass:</span><br><span style="color: hsl(0, 100%, 40%);">-                print "Test failed as expected."</span><br><span style="color: hsl(120, 100%, 40%);">+                print("Test failed as expected.")</span><br><span> </span><br><span>             self.passed = (did_pass == self.test_config.expect_pass)</span><br><span>             if abandon_test:</span><br><span>@@ -190,8 +196,8 @@</span><br><span>                     shutil.rmtree(absolute_dir)</span><br><span>                     os.remove(symlink_dir)</span><br><span>                 except:</span><br><span style="color: hsl(0, 100%, 40%);">-                    print "Unable to clean up directory for" \</span><br><span style="color: hsl(0, 100%, 40%);">-                          "test %s (non-fatal)" % self.test_name</span><br><span style="color: hsl(120, 100%, 40%);">+                    print("Unable to clean up directory for"</span><br><span style="color: hsl(120, 100%, 40%);">+                          "test %s (non-fatal)" % self.test_name)</span><br><span> </span><br><span>             self.__parse_run_output(self.stdout)</span><br><span>             if timedout:</span><br><span>@@ -202,13 +208,13 @@</span><br><span>                 status = 'passed'</span><br><span>             else:</span><br><span>                 status = 'failed'</span><br><span style="color: hsl(0, 100%, 40%);">-            pass_str = 'Test %s %s\n' % (cmd, status)</span><br><span style="color: hsl(0, 100%, 40%);">-            print pass_str</span><br><span style="color: hsl(120, 100%, 40%);">+            pass_str = 'Test %s %s\n' % (self.test_name, status)</span><br><span style="color: hsl(120, 100%, 40%);">+            print(pass_str)</span><br><span>             if self.options.syslog:</span><br><span>                 syslog.syslog(pass_str)</span><br><span> </span><br><span>         else:</span><br><span style="color: hsl(0, 100%, 40%);">-            print "FAILED TO EXECUTE %s, it must exist and be executable" % cmd</span><br><span style="color: hsl(120, 100%, 40%);">+            print("FAILED TO EXECUTE %s, it must exist and be executable" % cmd)</span><br><span>         self.time = time.time() - start_time</span><br><span> </span><br><span>     def _check_for_core(self):</span><br><span>@@ -237,7 +243,7 @@</span><br><span>         debug_level = email_config.get('debug', 0)</span><br><span> </span><br><span>         if not sender or len(recipients) == 0:</span><br><span style="color: hsl(0, 100%, 40%);">-            print "--email-on-crash requires sender and 1+ recipients"</span><br><span style="color: hsl(120, 100%, 40%);">+            print("--email-on-crash requires sender and 1+ recipients")</span><br><span>             return</span><br><span> </span><br><span>         with open(dest_file_name, 'r') as bt_file:</span><br><span>@@ -255,12 +261,12 @@</span><br><span>             send_email(smtp_server, sender, recipients, message,</span><br><span>                        debug=debug_level)</span><br><span>         except Exception as exception:</span><br><span style="color: hsl(0, 100%, 40%);">-            print "Failed to send email\nError: {0}".format(exception)</span><br><span style="color: hsl(120, 100%, 40%);">+            print("Failed to send email\nError: {0}".format(exception))</span><br><span> </span><br><span>     def _archive_core_dumps(self, core_dumps):</span><br><span>         for core in core_dumps:</span><br><span>             if not os.path.exists(core):</span><br><span style="color: hsl(0, 100%, 40%);">-                print "Unable to find core dump file %s, skipping" % core</span><br><span style="color: hsl(120, 100%, 40%);">+                print("Unable to find core dump file %s, skipping" % core)</span><br><span>                 continue</span><br><span>             random_num = random.randint(0, 16000)</span><br><span>             dest_dir = "./logs/%s" % self.test_relpath</span><br><span>@@ -275,36 +281,36 @@</span><br><span>                        "-ex", "thread apply all bt",</span><br><span>                        "--batch",</span><br><span>                        "-c", core]</span><br><span style="color: hsl(0, 100%, 40%);">-            print "Running %s" % (" ".join(gdb_cmd),)</span><br><span style="color: hsl(120, 100%, 40%);">+            print("Running %s" % (" ".join(gdb_cmd),))</span><br><span>             try:</span><br><span>                 res = subprocess.call(gdb_cmd, stdout=dest_file, stderr=subprocess.STDOUT)</span><br><span>                 if res != 0:</span><br><span style="color: hsl(0, 100%, 40%);">-                    print "error analyzing core dump; gdb exited with %d" % res</span><br><span style="color: hsl(120, 100%, 40%);">+                    print("error analyzing core dump; gdb exited with %d" % res)</span><br><span>                 # Copy the backtrace over to the logs</span><br><span style="color: hsl(0, 100%, 40%);">-                print "Archived backtrace: {0}".format(dest_file_name)</span><br><span style="color: hsl(120, 100%, 40%);">+                print("Archived backtrace: {0}".format(dest_file_name))</span><br><span> </span><br><span>                 if self.options.email_on_crash:</span><br><span>                     self._email_crash_report(dest_file_name)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-            except OSError, ose:</span><br><span style="color: hsl(0, 100%, 40%);">-                print "OSError ([%d]: %s) occurred while executing %r" % \</span><br><span style="color: hsl(0, 100%, 40%);">-                    (ose.errno, ose.strerror, gdb_cmd)</span><br><span style="color: hsl(120, 100%, 40%);">+            except OSError as ose:</span><br><span style="color: hsl(120, 100%, 40%);">+                print("OSError ([%d]: %s) occurred while executing %r" %</span><br><span style="color: hsl(120, 100%, 40%);">+                    (ose.errno, ose.strerror, gdb_cmd))</span><br><span>             except:</span><br><span style="color: hsl(0, 100%, 40%);">-                print "Unknown exception occurred while executing %r" % (gdb_cmd,)</span><br><span style="color: hsl(120, 100%, 40%);">+                print("Unknown exception occurred while executing %r" % (gdb_cmd,))</span><br><span>             finally:</span><br><span>                 dest_file.close()</span><br><span>                 if self.options.keep_core:</span><br><span>                     try:</span><br><span>                         dst_core = os.path.join(dest_dir, "core_{0}".format(random_num))</span><br><span>                         shutil.copy(core, dst_core)</span><br><span style="color: hsl(0, 100%, 40%);">-                        print "Archived core file: {0}".format(dst_core)</span><br><span style="color: hsl(120, 100%, 40%);">+                        print("Archived core file: {0}".format(dst_core))</span><br><span>                     except Exception as e:</span><br><span style="color: hsl(0, 100%, 40%);">-                        print "Error occurred while copying core: {0}".format(e)</span><br><span style="color: hsl(120, 100%, 40%);">+                        print("Error occurred while copying core: {0}".format(e))</span><br><span>                 try:</span><br><span>                     os.unlink(core)</span><br><span style="color: hsl(0, 100%, 40%);">-                except OSError, e:</span><br><span style="color: hsl(0, 100%, 40%);">-                    print "Error removing core file: %s: " \</span><br><span style="color: hsl(0, 100%, 40%);">-                          "Beware of the stale core file in CWD!" % (e,)</span><br><span style="color: hsl(120, 100%, 40%);">+                except OSError as e:</span><br><span style="color: hsl(120, 100%, 40%);">+                    print("Error removing core file: %s: "</span><br><span style="color: hsl(120, 100%, 40%);">+                          "Beware of the stale core file in CWD!" % (e,))</span><br><span> </span><br><span>     def _find_run_dirs(self):</span><br><span>         test_run_dir = os.path.join(Asterisk.test_suite_root,</span><br><span>@@ -387,7 +393,7 @@</span><br><span>                     res = subprocess.call(refcounter,</span><br><span>                                           stdout=dest_file,</span><br><span>                                           stderr=subprocess.STDOUT)</span><br><span style="color: hsl(0, 100%, 40%);">-                except Exception, e:</span><br><span style="color: hsl(120, 100%, 40%);">+                except Exception as e:</span><br><span>                     self.stdout_print("Exception occurred while processing REF_DEBUG")</span><br><span>                 finally:</span><br><span>                     dest_file.close()</span><br><span>@@ -415,10 +421,10 @@</span><br><span>                 srcfile = os.path.join(src_dir, filename)</span><br><span>                 if os.path.exists(srcfile):</span><br><span>                     hardlink_or_copy(srcfile, os.path.join(dest_dir, filename))</span><br><span style="color: hsl(0, 100%, 40%);">-            except Exception, e:</span><br><span style="color: hsl(0, 100%, 40%);">-                print "Exception occurred while archiving file '%s' to %s: %s" % (</span><br><span style="color: hsl(120, 100%, 40%);">+            except Exception as e:</span><br><span style="color: hsl(120, 100%, 40%);">+                print("Exception occurred while archiving file '%s' to %s: %s" % (</span><br><span>                     srcfile, dest_dir, e</span><br><span style="color: hsl(0, 100%, 40%);">-                )</span><br><span style="color: hsl(120, 100%, 40%);">+                ))</span><br><span> </span><br><span>     def _archive_logs(self):</span><br><span>         (run_num, run_dir, archive_dir) = self._find_run_dirs()</span><br><span>@@ -519,35 +525,35 @@</span><br><span>         tags.sort(key=str.lower)</span><br><span>         maxwidth = max(len(t) for t in tags)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        print "Available test tags:"</span><br><span style="color: hsl(120, 100%, 40%);">+        print("Available test tags:")</span><br><span>         tags = chunks(tags, 3)</span><br><span>         for tag in tags:</span><br><span style="color: hsl(0, 100%, 40%);">-            print "\t%-*s     %-*s     %-*s" % (</span><br><span style="color: hsl(120, 100%, 40%);">+            print("\t%-*s     %-*s     %-*s" % (</span><br><span>                 maxwidth, tag[0],</span><br><span>                 maxwidth, len(tag) > 1 and tag[1] or '',</span><br><span style="color: hsl(0, 100%, 40%);">-                maxwidth, len(tag) > 2 and tag[2] or '')</span><br><span style="color: hsl(120, 100%, 40%);">+                maxwidth, len(tag) > 2 and tag[2] or ''))</span><br><span> </span><br><span>     def list_tests(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        print "Configured tests:"</span><br><span style="color: hsl(120, 100%, 40%);">+        print("Configured tests:")</span><br><span>         i = 1</span><br><span>         for t in self.tests:</span><br><span style="color: hsl(0, 100%, 40%);">-            print "%.3d) %s" % (i, t.test_config.test_name)</span><br><span style="color: hsl(0, 100%, 40%);">-            print "      --> Summary: %s" % t.test_config.summary</span><br><span style="color: hsl(120, 100%, 40%);">+            print("%.3d) %s" % (i, t.test_config.test_name))</span><br><span style="color: hsl(120, 100%, 40%);">+            print("      --> Summary: %s" % t.test_config.summary)</span><br><span>             if t.test_config.skip is not None:</span><br><span style="color: hsl(0, 100%, 40%);">-                print "      --> Skip: %s" % t.test_config.skip</span><br><span style="color: hsl(120, 100%, 40%);">+                print("      --> Skip: %s" % t.test_config.skip)</span><br><span>             if t.test_config.features:</span><br><span style="color: hsl(0, 100%, 40%);">-                print "      --> Features:"</span><br><span style="color: hsl(120, 100%, 40%);">+                print("      --> Features:")</span><br><span>                 for feature_name in t.test_config.features:</span><br><span style="color: hsl(0, 100%, 40%);">-                    print "        --> %s: -- Met: %s" % \</span><br><span style="color: hsl(0, 100%, 40%);">-                        (feature_name, str(t.test_config.feature_check[feature_name]))</span><br><span style="color: hsl(120, 100%, 40%);">+                    print("        --> %s: -- Met: %s" %</span><br><span style="color: hsl(120, 100%, 40%);">+                        (feature_name, str(t.test_config.feature_check[feature_name])))</span><br><span>             if t.test_config.tags:</span><br><span style="color: hsl(0, 100%, 40%);">-                print "      --> Tags: %s" % str(t.test_config.tags)</span><br><span style="color: hsl(120, 100%, 40%);">+                print("      --> Tags: %s" % str(t.test_config.tags))</span><br><span>             for d in t.test_config.deps:</span><br><span>                 if d.version:</span><br><span style="color: hsl(0, 100%, 40%);">-                    print "      --> Dependency: %s" % (d.name)</span><br><span style="color: hsl(0, 100%, 40%);">-                    print "        --> Version: %s -- Met: %s" % (d.version, str(d.met))</span><br><span style="color: hsl(120, 100%, 40%);">+                    print("      --> Dependency: %s" % (d.name))</span><br><span style="color: hsl(120, 100%, 40%);">+                    print("        --> Version: %s -- Met: %s" % (d.version, str(d.met)))</span><br><span>                 else:</span><br><span style="color: hsl(0, 100%, 40%);">-                    print "      --> Dependency: %s -- Met: %s" % (d.name, str(d.met))</span><br><span style="color: hsl(120, 100%, 40%);">+                    print("      --> Dependency: %s -- Met: %s" % (d.name, str(d.met)))</span><br><span> </span><br><span>             i += 1</span><br><span> </span><br><span>@@ -571,7 +577,7 @@</span><br><span>                         else:</span><br><span>                             deps += ("%s" % d.name)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-            print "%04d %s %s%s" % (i, flag, t.test_config.test_name, deps)</span><br><span style="color: hsl(120, 100%, 40%);">+            print("%04d %s %s%s" % (i, flag, t.test_config.test_name, deps))</span><br><span>             i += 1</span><br><span> </span><br><span>     def run(self):</span><br><span>@@ -587,8 +593,8 @@</span><br><span>                     if excluded in t.test_name:</span><br><span>                         continue</span><br><span>             i += 1</span><br><span style="color: hsl(0, 100%, 40%);">-        print "Tests to run: %d * %d time(s) = %d  Maximum test inactivity time: %d sec." % \</span><br><span style="color: hsl(0, 100%, 40%);">-            (i, self.options.number, i * self.options.number, (self.options.timeout / 1000))</span><br><span style="color: hsl(120, 100%, 40%);">+        print("Tests to run: %d * %d time(s) = %d  Maximum test inactivity time: %d sec." %</span><br><span style="color: hsl(120, 100%, 40%);">+            (i, self.options.number, i * self.options.number, (self.options.timeout / 1000)))</span><br><span> </span><br><span>         for t in self.tests:</span><br><span>             if abandon_test_suite:</span><br><span>@@ -596,18 +602,18 @@</span><br><span> </span><br><span>             if t.can_run is False:</span><br><span>                 if t.test_config.skip is not None:</span><br><span style="color: hsl(0, 100%, 40%);">-                    print "--> %s ... skipped '%s'" % (t.test_name, t.test_config.skip)</span><br><span style="color: hsl(120, 100%, 40%);">+                    print("--> %s ... skipped '%s'" % (t.test_name, t.test_config.skip))</span><br><span>                     t.skipped_reason = t.test_config.skip</span><br><span>                     self.total_skipped += 1</span><br><span>                     continue</span><br><span style="color: hsl(0, 100%, 40%);">-                print "--> Cannot run test '%s'" % t.test_name</span><br><span style="color: hsl(120, 100%, 40%);">+                print("--> Cannot run test '%s'" % t.test_name)</span><br><span>                 for f in t.test_config.features:</span><br><span style="color: hsl(0, 100%, 40%);">-                    print "--- --> Version Feature: %s - %s" % (</span><br><span style="color: hsl(0, 100%, 40%);">-                        f, str(t.test_config.feature_check[f]))</span><br><span style="color: hsl(0, 100%, 40%);">-                print "--- --> Tags: %s" % (t.test_config.tags)</span><br><span style="color: hsl(120, 100%, 40%);">+                    print("--- --> Version Feature: %s - %s" % (</span><br><span style="color: hsl(120, 100%, 40%);">+                        f, str(t.test_config.feature_check[f])))</span><br><span style="color: hsl(120, 100%, 40%);">+                print("--- --> Tags: %s" % (t.test_config.tags))</span><br><span>                 for d in t.test_config.deps:</span><br><span style="color: hsl(0, 100%, 40%);">-                    print "--- --> Dependency: %s - %s" % (d.name, str(d.met))</span><br><span style="color: hsl(0, 100%, 40%);">-                print</span><br><span style="color: hsl(120, 100%, 40%);">+                    print("--- --> Dependency: %s - %s" % (d.name, str(d.met)))</span><br><span style="color: hsl(120, 100%, 40%);">+                print("")</span><br><span>                 self.total_skipped += 1</span><br><span>                 t.skipped_reason = "Failed dependency"</span><br><span>                 continue</span><br><span>@@ -615,14 +621,14 @@</span><br><span>                 exclude = False</span><br><span>                 for excluded in self.global_config.excluded_tests:</span><br><span>                     if excluded in t.test_name:</span><br><span style="color: hsl(0, 100%, 40%);">-                        print "--- ---> Excluded test: %s" % excluded</span><br><span style="color: hsl(120, 100%, 40%);">+                        print("--- ---> Excluded test: %s" % excluded)</span><br><span>                         exclude = True</span><br><span>                 if exclude:</span><br><span>                     self.total_skipped += 1</span><br><span>                     continue</span><br><span> </span><br><span>             running_str = "--> Running test '%s' ..." % t.test_name</span><br><span style="color: hsl(0, 100%, 40%);">-            print running_str</span><br><span style="color: hsl(120, 100%, 40%);">+            print(running_str)</span><br><span>             if self.options.syslog:</span><br><span>                 syslog.syslog(running_str)</span><br><span> </span><br><span>@@ -630,16 +636,16 @@</span><br><span>                 t.passed = True</span><br><span>             else:</span><br><span>                 # Establish Preconditions</span><br><span style="color: hsl(0, 100%, 40%);">-                print "Making sure Asterisk isn't running ..."</span><br><span style="color: hsl(120, 100%, 40%);">+                print("Making sure Asterisk isn't running ...")</span><br><span>                 if os.system("if pidof asterisk >/dev/null; then "</span><br><span>                              "killall -9 asterisk >/dev/null 2>&1; "</span><br><span>                              "sleep 1; ! pidof asterisk >/dev/null; fi"):</span><br><span style="color: hsl(0, 100%, 40%);">-                    print "Could not kill asterisk."</span><br><span style="color: hsl(0, 100%, 40%);">-                print "Making sure SIPp isn't running..."</span><br><span style="color: hsl(120, 100%, 40%);">+                    print("Could not kill asterisk.")</span><br><span style="color: hsl(120, 100%, 40%);">+                print("Making sure SIPp isn't running...")</span><br><span>                 if os.system("if pidof sipp >/dev/null; then "</span><br><span>                              "killall -9 sipp >/dev/null 2>&1; "</span><br><span>                              "sleep 1; ! pidof sipp >/dev/null; fi"):</span><br><span style="color: hsl(0, 100%, 40%);">-                    print "Could not kill sipp."</span><br><span style="color: hsl(120, 100%, 40%);">+                    print("Could not kill sipp.")</span><br><span>                 # XXX TODO Hard coded path, gross.</span><br><span>                 os.system("rm -f /var/run/asterisk/asterisk.ctl")</span><br><span>                 os.system("rm -f /var/run/asterisk/asterisk.pid")</span><br><span>@@ -668,12 +674,12 @@</span><br><span>             (0x86, 0x9f),</span><br><span>         ]</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        char_list = []</span><br><span style="color: hsl(120, 100%, 40%);">+        tbl = {}</span><br><span>         for r in bad_chars:</span><br><span>             # we do +1 here to include the last item</span><br><span>             for i in range(r[0], r[1] + 1):</span><br><span style="color: hsl(0, 100%, 40%);">-                char_list.append(chr(i))</span><br><span style="color: hsl(0, 100%, 40%);">-        return data.translate(None, ''.join(char_list))</span><br><span style="color: hsl(120, 100%, 40%);">+                tbl[chr(i)] = None</span><br><span style="color: hsl(120, 100%, 40%);">+        return data.translate(tbl)</span><br><span> </span><br><span>     def write_results_xml(self, doc, root):</span><br><span> </span><br><span>@@ -728,10 +734,10 @@</span><br><span>     except IOError:</span><br><span>         # Ignore errors for the optional tests/custom folder.</span><br><span>         if path != "tests/custom/tests.yaml":</span><br><span style="color: hsl(0, 100%, 40%);">-            print "Failed to open %s" % path</span><br><span style="color: hsl(120, 100%, 40%);">+            print("Failed to open %s" % path)</span><br><span>         return None</span><br><span>     except:</span><br><span style="color: hsl(0, 100%, 40%);">-        print "Unexpected error: %s" % sys.exc_info()[0]</span><br><span style="color: hsl(120, 100%, 40%);">+        print("Unexpected error: %s" % sys.exc_info()[0])</span><br><span>         return None</span><br><span> </span><br><span>     config = yaml.load(f)</span><br><span>@@ -747,7 +753,7 @@</span><br><span>     """</span><br><span>     global abandon_test_suite</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    print "SIGUSR1 received; stopping test suite after current test..."</span><br><span style="color: hsl(120, 100%, 40%);">+    print("SIGUSR1 received; stopping test suite after current test...")</span><br><span>     abandon_test_suite = True</span><br><span> </span><br><span> </span><br><span>@@ -760,7 +766,7 @@</span><br><span>     global abandon_test</span><br><span>     global abandon_test_suite</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    print "SIGTREM received; abandoning current test and stopping..."</span><br><span style="color: hsl(120, 100%, 40%);">+    print("SIGTREM received; abandoning current test and stopping...")</span><br><span>     abandon_test = True</span><br><span>     abandon_test_suite = True</span><br><span> </span><br><span>@@ -877,8 +883,8 @@</span><br><span> </span><br><span>     if options.valgrind:</span><br><span>         if not ET:</span><br><span style="color: hsl(0, 100%, 40%);">-            print "python lxml module not loaded, text summaries " \</span><br><span style="color: hsl(0, 100%, 40%);">-                  "from valgrind will not be produced.\n"</span><br><span style="color: hsl(120, 100%, 40%);">+            print("python lxml module not loaded, text summaries "</span><br><span style="color: hsl(120, 100%, 40%);">+                  "from valgrind will not be produced.\n")</span><br><span>         os.environ["VALGRIND_ENABLE"] = "true"</span><br><span> </span><br><span>     dom = xml.dom.getDOMImplementation()</span><br><span>@@ -894,7 +900,7 @@</span><br><span> </span><br><span>         running_str = "Running tests for Asterisk (run {0} of {1})...\n".format(</span><br><span>             iteration + 1, options.number)</span><br><span style="color: hsl(0, 100%, 40%);">-        print running_str</span><br><span style="color: hsl(120, 100%, 40%);">+        print(running_str)</span><br><span>         if options.syslog:</span><br><span>             syslog.syslog(running_str)</span><br><span> </span><br><span>@@ -903,22 +909,22 @@</span><br><span> </span><br><span>         # If exactly one test was requested, then skip the summary.</span><br><span>         if len(test_suite.tests) != 1:</span><br><span style="color: hsl(0, 100%, 40%);">-            print "\n=== TEST RESULTS ===\n"</span><br><span style="color: hsl(0, 100%, 40%);">-            print "PATH: %s\n" % os.getenv("PATH")</span><br><span style="color: hsl(120, 100%, 40%);">+            print("\n=== TEST RESULTS ===\n")</span><br><span style="color: hsl(120, 100%, 40%);">+            print("PATH: %s\n" % os.getenv("PATH"))</span><br><span>             for t in test_suite.tests:</span><br><span>                 sys.stdout.write("--> %s --- " % t.test_name)</span><br><span>                 if t.did_run is False:</span><br><span style="color: hsl(0, 100%, 40%);">-                    print "SKIPPED"</span><br><span style="color: hsl(120, 100%, 40%);">+                    print("SKIPPED")</span><br><span>                     for d in t.test_config.deps:</span><br><span style="color: hsl(0, 100%, 40%);">-                        print "      --> Dependency: %s -- Met: %s" % (d.name, str(d.met))</span><br><span style="color: hsl(120, 100%, 40%);">+                        print("      --> Dependency: %s -- Met: %s" % (d.name, str(d.met)))</span><br><span>                     if options.tags:</span><br><span>                         for t in t.test_config.tags:</span><br><span style="color: hsl(0, 100%, 40%);">-                            print "      --> Tag: %s -- Met: %s" % (t, str(t in options.tags))</span><br><span style="color: hsl(120, 100%, 40%);">+                            print("      --> Tag: %s -- Met: %s" % (t, str(t in options.tags)))</span><br><span>                     continue</span><br><span>                 if t.passed is True:</span><br><span style="color: hsl(0, 100%, 40%);">-                    print "PASSED"</span><br><span style="color: hsl(120, 100%, 40%);">+                    print("PASSED")</span><br><span>                 else:</span><br><span style="color: hsl(0, 100%, 40%);">-                    print "FAILED"</span><br><span style="color: hsl(120, 100%, 40%);">+                    print("FAILED")</span><br><span> </span><br><span>         iteration += 1</span><br><span> </span><br><span>@@ -929,11 +935,11 @@</span><br><span>         with open(TEST_RESULTS, "w") as f:</span><br><span>             doc.writexml(f, addindent="  ", newl="\n", encoding="utf-8")</span><br><span>     except IOError:</span><br><span style="color: hsl(0, 100%, 40%);">-        print "Failed to open test results output file: %s" % TEST_RESULTS</span><br><span style="color: hsl(120, 100%, 40%);">+        print("Failed to open test results output file: %s" % TEST_RESULTS)</span><br><span>     except:</span><br><span style="color: hsl(0, 100%, 40%);">-        print "Unexpected error: %s" % sys.exc_info()[0]</span><br><span style="color: hsl(0, 100%, 40%);">-    print "\n"</span><br><span style="color: hsl(0, 100%, 40%);">-    print doc.toprettyxml("  ", encoding="utf-8")</span><br><span style="color: hsl(120, 100%, 40%);">+        print("Unexpected error: %s" % sys.exc_info()[0])</span><br><span style="color: hsl(120, 100%, 40%);">+    print("\n")</span><br><span style="color: hsl(120, 100%, 40%);">+    print(doc.toprettyxml("  ", encoding="utf-8").decode('utf-8', 'ignore'))</span><br><span> </span><br><span>     if options.syslog:</span><br><span>         syslog.syslog("All tests concluded")</span><br><span>@@ -954,7 +960,7 @@</span><br><span> </span><br><span>     try:</span><br><span>         os.link(source, destination)</span><br><span style="color: hsl(0, 100%, 40%);">-    except OSError, e:</span><br><span style="color: hsl(120, 100%, 40%);">+    except OSError as e:</span><br><span>         # Different partitions can cause hard links to fail (error 18),</span><br><span>         # if there's a different error, bail out immediately.</span><br><span>         if e.args[0] != errno.EXDEV:</span><br><span>diff --git a/self_test b/self_test</span><br><span>index 1936014..045c090 100755</span><br><span>--- a/self_test</span><br><span>+++ b/self_test</span><br><span>@@ -28,17 +28,3 @@</span><br><span>       run_test $i python2 $PYTHON2</span><br><span>         [ "${i#test2}" = "${i}" ] && run_test $i python3 $PYTHON3</span><br><span> done</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-# Temporary code for running unit tests that are not compatible with python3</span><br><span style="color: hsl(0, 100%, 40%);">-run_legacy_test() {</span><br><span style="color: hsl(0, 100%, 40%);">-    # Arguments: test_name python_name python_bin</span><br><span style="color: hsl(0, 100%, 40%);">-   if test -n "$3"; then</span><br><span style="color: hsl(0, 100%, 40%);">-         echo " ==> Executing $1 ($2)"</span><br><span style="color: hsl(0, 100%, 40%);">-              $3 lib/python/asterisk/${1}.py</span><br><span style="color: hsl(0, 100%, 40%);">-  fi</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-for i in buildoptions channel_test_condition sippversion; do</span><br><span style="color: hsl(0, 100%, 40%);">- run_legacy_test $i python $PYTHON</span><br><span style="color: hsl(0, 100%, 40%);">-       run_legacy_test $i python2 $PYTHON2</span><br><span style="color: hsl(0, 100%, 40%);">-done</span><br><span>diff --git a/tests/cdr/cdr-tests.py b/tests/cdr/cdr-tests.py</span><br><span>index a284673..9090045 100644</span><br><span>--- a/tests/cdr/cdr-tests.py</span><br><span>+++ b/tests/cdr/cdr-tests.py</span><br><span>@@ -13,8 +13,8 @@</span><br><span> import time</span><br><span> </span><br><span> sys.path.append("lib/python")</span><br><span style="color: hsl(0, 100%, 40%);">-from cdr import CDRModule</span><br><span style="color: hsl(0, 100%, 40%);">-from cdr import AsteriskCSVCDR</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.cdr import CDRModule</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.cdr import AsteriskCSVCDR</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>diff --git a/tests/cdr/sqlite3/cdr_sqlite3.py b/tests/cdr/sqlite3/cdr_sqlite3.py</span><br><span>index 1def05e..32ed786 100644</span><br><span>--- a/tests/cdr/sqlite3/cdr_sqlite3.py</span><br><span>+++ b/tests/cdr/sqlite3/cdr_sqlite3.py</span><br><span>@@ -14,7 +14,7 @@</span><br><span> import re</span><br><span> </span><br><span> sys.path.append("lib/python")</span><br><span style="color: hsl(0, 100%, 40%);">-from config import ConfigFile</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.config import ConfigFile</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>diff --git a/tests/channels/SIP/tcpauthlimit/sipp_scenario.py b/tests/channels/SIP/tcpauthlimit/sipp_scenario.py</span><br><span>index 76a21aa..55a26f5 100644</span><br><span>--- a/tests/channels/SIP/tcpauthlimit/sipp_scenario.py</span><br><span>+++ b/tests/channels/SIP/tcpauthlimit/sipp_scenario.py</span><br><span>@@ -9,7 +9,7 @@</span><br><span> </span><br><span> import logging</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from sipp import SIPpScenario</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.sipp import SIPpScenario</span><br><span> from twisted.internet import defer</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span>diff --git a/tests/channels/pjsip/registration/inbound/nominal/contact_acl/ipv4/scenario_generator.py b/tests/channels/pjsip/registration/inbound/nominal/contact_acl/ipv4/scenario_generator.py</span><br><span>index 2e2357a..4fcb67a 100755</span><br><span>--- a/tests/channels/pjsip/registration/inbound/nominal/contact_acl/ipv4/scenario_generator.py</span><br><span>+++ b/tests/channels/pjsip/registration/inbound/nominal/contact_acl/ipv4/scenario_generator.py</span><br><span>@@ -12,8 +12,8 @@</span><br><span> </span><br><span> sys.path.append("lib/python")</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-import test_suite_utils</span><br><span style="color: hsl(0, 100%, 40%);">-from sipp import ScenarioGenerator</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk import test_suite_utils</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.sipp import ScenarioGenerator</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>diff --git a/tests/channels/pjsip/registration/inbound/nominal/contact_acl/ipv6/scenario_generator.py b/tests/channels/pjsip/registration/inbound/nominal/contact_acl/ipv6/scenario_generator.py</span><br><span>index 2a52c96..541ba62 100755</span><br><span>--- a/tests/channels/pjsip/registration/inbound/nominal/contact_acl/ipv6/scenario_generator.py</span><br><span>+++ b/tests/channels/pjsip/registration/inbound/nominal/contact_acl/ipv6/scenario_generator.py</span><br><span>@@ -12,8 +12,8 @@</span><br><span> </span><br><span> sys.path.append("lib/python")</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-import test_suite_utils</span><br><span style="color: hsl(0, 100%, 40%);">-from sipp import ScenarioGenerator</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk import test_suite_utils</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.sipp import ScenarioGenerator</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>diff --git a/tests/channels/pjsip/subscriptions/presence/verify_bodies/presence.py b/tests/channels/pjsip/subscriptions/presence/verify_bodies/presence.py</span><br><span>index 143ea42..bbff8f8 100644</span><br><span>--- a/tests/channels/pjsip/subscriptions/presence/verify_bodies/presence.py</span><br><span>+++ b/tests/channels/pjsip/subscriptions/presence/verify_bodies/presence.py</span><br><span>@@ -15,7 +15,7 @@</span><br><span> </span><br><span> sys.path.append('lib/python')</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from pcap import VOIPListener</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.pcap import VOIPListener</span><br><span> from twisted.internet import reactor</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span>diff --git a/tests/channels/pjsip/subscriptions/rls/rls_test.py b/tests/channels/pjsip/subscriptions/rls/rls_test.py</span><br><span>index 9c4051c..08d0567 100644</span><br><span>--- a/tests/channels/pjsip/subscriptions/rls/rls_test.py</span><br><span>+++ b/tests/channels/pjsip/subscriptions/rls/rls_test.py</span><br><span>@@ -13,7 +13,7 @@</span><br><span> sys.path.append("lib/python")</span><br><span> sys.path.append("tests/channels/pjsip/subscriptions/rls")</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from pcap import VOIPProxy</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.pcap import VOIPProxy</span><br><span> from rls_element import RLSPacket</span><br><span> from rls_validation import ValidationInfo</span><br><span> from twisted.internet import reactor</span><br><span>diff --git a/tests/channels/pjsip/transfers/attended_transfer/nominal/packet_sniffer.py b/tests/channels/pjsip/transfers/attended_transfer/nominal/packet_sniffer.py</span><br><span>index a96a661..b49f268 100755</span><br><span>--- a/tests/channels/pjsip/transfers/attended_transfer/nominal/packet_sniffer.py</span><br><span>+++ b/tests/channels/pjsip/transfers/attended_transfer/nominal/packet_sniffer.py</span><br><span>@@ -35,8 +35,8 @@</span><br><span> </span><br><span> sys.path.append('lib/python')</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from phones import PjsuaPhoneController</span><br><span style="color: hsl(0, 100%, 40%);">-from pcap import VOIPListener</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.phones import PjsuaPhoneController</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.pcap import VOIPListener</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>diff --git a/tests/codecs/audio_analyzer.py b/tests/codecs/audio_analyzer.py</span><br><span>index 9903c6d..e656777 100644</span><br><span>--- a/tests/codecs/audio_analyzer.py</span><br><span>+++ b/tests/codecs/audio_analyzer.py</span><br><span>@@ -13,8 +13,8 @@</span><br><span> import logging</span><br><span> from twisted.internet import reactor</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-import ami</span><br><span style="color: hsl(0, 100%, 40%);">-import ari</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk import ami</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk import ari</span><br><span> import tonetest</span><br><span> </span><br><span> sys.path.append("lib/python")</span><br><span>diff --git a/tests/codecs/rtp_analyzer.py b/tests/codecs/rtp_analyzer.py</span><br><span>index e20cde3..74f161a 100644</span><br><span>--- a/tests/codecs/rtp_analyzer.py</span><br><span>+++ b/tests/codecs/rtp_analyzer.py</span><br><span>@@ -13,7 +13,7 @@</span><br><span> </span><br><span> sys.path.append("lib/python")</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-import pcap</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk import pcap</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>diff --git a/tests/hep/hep_capture_node.py b/tests/hep/hep_capture_node.py</span><br><span>index f663056..3e7feae 100644</span><br><span>--- a/tests/hep/hep_capture_node.py</span><br><span>+++ b/tests/hep/hep_capture_node.py</span><br><span>@@ -28,7 +28,7 @@</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from test_suite_utils import all_match</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.test_suite_utils import all_match</span><br><span> </span><br><span> def enum(**enums):</span><br><span>     """Make an enumeration out of the passed in values"""</span><br><span>diff --git a/tests/manager/config/ManagerConfigTest.py b/tests/manager/config/ManagerConfigTest.py</span><br><span>index b7dbd8a..fe2e65f 100644</span><br><span>--- a/tests/manager/config/ManagerConfigTest.py</span><br><span>+++ b/tests/manager/config/ManagerConfigTest.py</span><br><span>@@ -13,8 +13,8 @@</span><br><span> </span><br><span> sys.path.append("lib/python")</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from test_case import TestCase</span><br><span style="color: hsl(0, 100%, 40%);">-from syncami import SyncAMI</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.test_case import TestCase</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.syncami import SyncAMI</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>diff --git a/tests/manager/device_state_changed/ami_device_state.py b/tests/manager/device_state_changed/ami_device_state.py</span><br><span>index a3ac5f3..96d8021 100644</span><br><span>--- a/tests/manager/device_state_changed/ami_device_state.py</span><br><span>+++ b/tests/manager/device_state_changed/ami_device_state.py</span><br><span>@@ -8,7 +8,7 @@</span><br><span> '''</span><br><span> </span><br><span> import logging</span><br><span style="color: hsl(0, 100%, 40%);">-from test_case import TestCase</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.test_case import TestCase</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>diff --git a/tests/manager/device_state_list/ami_device_state_list.py b/tests/manager/device_state_list/ami_device_state_list.py</span><br><span>index 5abd104..105780e 100755</span><br><span>--- a/tests/manager/device_state_list/ami_device_state_list.py</span><br><span>+++ b/tests/manager/device_state_list/ami_device_state_list.py</span><br><span>@@ -9,7 +9,7 @@</span><br><span> """</span><br><span> </span><br><span> import logging</span><br><span style="color: hsl(0, 100%, 40%);">-from test_case import TestCase</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.test_case import TestCase</span><br><span> from twisted.internet import defer</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span>diff --git a/tests/manager/exten_state_list/ami_exten_state_list.py b/tests/manager/exten_state_list/ami_exten_state_list.py</span><br><span>index c9449f4..90bdc4f 100755</span><br><span>--- a/tests/manager/exten_state_list/ami_exten_state_list.py</span><br><span>+++ b/tests/manager/exten_state_list/ami_exten_state_list.py</span><br><span>@@ -9,7 +9,7 @@</span><br><span> """</span><br><span> </span><br><span> import logging</span><br><span style="color: hsl(0, 100%, 40%);">-from test_case import TestCase</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.test_case import TestCase</span><br><span> from twisted.internet import defer</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span>diff --git a/tests/manager/presence_state_changed/ami_presence_state.py b/tests/manager/presence_state_changed/ami_presence_state.py</span><br><span>index e6861fc..d8ac551 100644</span><br><span>--- a/tests/manager/presence_state_changed/ami_presence_state.py</span><br><span>+++ b/tests/manager/presence_state_changed/ami_presence_state.py</span><br><span>@@ -8,7 +8,7 @@</span><br><span> '''</span><br><span> </span><br><span> import logging</span><br><span style="color: hsl(0, 100%, 40%);">-from test_case import TestCase</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.test_case import TestCase</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>diff --git a/tests/manager/presence_state_list/ami_presence_state_list.py b/tests/manager/presence_state_list/ami_presence_state_list.py</span><br><span>index 13c16c7..f67c386 100755</span><br><span>--- a/tests/manager/presence_state_list/ami_presence_state_list.py</span><br><span>+++ b/tests/manager/presence_state_list/ami_presence_state_list.py</span><br><span>@@ -9,7 +9,7 @@</span><br><span> """</span><br><span> </span><br><span> import logging</span><br><span style="color: hsl(0, 100%, 40%);">-from test_case import TestCase</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.test_case import TestCase</span><br><span> from twisted.internet import defer</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span>diff --git a/tests/pbx/manager_extensions/ami_extension_control.py b/tests/pbx/manager_extensions/ami_extension_control.py</span><br><span>index d4ac26c..01e53af 100755</span><br><span>--- a/tests/pbx/manager_extensions/ami_extension_control.py</span><br><span>+++ b/tests/pbx/manager_extensions/ami_extension_control.py</span><br><span>@@ -8,7 +8,7 @@</span><br><span> '''</span><br><span> </span><br><span> import logging</span><br><span style="color: hsl(0, 100%, 40%);">-from test_case import TestCase</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.test_case import TestCase</span><br><span> from twisted.internet import defer</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span>diff --git a/tests/rest_api/external_interaction/attended_transfer/attended_transfer.py b/tests/rest_api/external_interaction/attended_transfer/attended_transfer.py</span><br><span>index d03fbfe..6b7790c 100644</span><br><span>--- a/tests/rest_api/external_interaction/attended_transfer/attended_transfer.py</span><br><span>+++ b/tests/rest_api/external_interaction/attended_transfer/attended_transfer.py</span><br><span>@@ -9,7 +9,7 @@</span><br><span> import logging</span><br><span> from sys import path</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from sipp import SIPpScenario</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.sipp import SIPpScenario</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>diff --git a/tests/rest_api/external_interaction/blind_transfer/call_transfer.py b/tests/rest_api/external_interaction/blind_transfer/call_transfer.py</span><br><span>index 23f1a7b..ddef074 100755</span><br><span>--- a/tests/rest_api/external_interaction/blind_transfer/call_transfer.py</span><br><span>+++ b/tests/rest_api/external_interaction/blind_transfer/call_transfer.py</span><br><span>@@ -16,7 +16,7 @@</span><br><span> import pjsua as pj</span><br><span> from twisted.internet import reactor</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-import pjsua_mod</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk import pjsua_mod</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> THIS_FILE = os.path.basename(__file__)</span><br><span>diff --git a/tests/rest_api/message/message_modules.py b/tests/rest_api/message/message_modules.py</span><br><span>index 2e4cdd4..c2d8f2e 100644</span><br><span>--- a/tests/rest_api/message/message_modules.py</span><br><span>+++ b/tests/rest_api/message/message_modules.py</span><br><span>@@ -14,7 +14,7 @@</span><br><span> </span><br><span> from twisted.internet import defer</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from sipp import SIPpScenario</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.sipp import SIPpScenario</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span>diff --git a/usage.py b/usage.py</span><br><span>index 16dea94..9e7c33f 100755</span><br><span>--- a/usage.py</span><br><span>+++ b/usage.py</span><br><span>@@ -70,7 +70,7 @@</span><br><span>     def occurances(self, **kwargs):</span><br><span>         match = []</span><br><span>         for test in self.tests:</span><br><span style="color: hsl(0, 100%, 40%);">-            for key, value in kwargs.iteritems():</span><br><span style="color: hsl(120, 100%, 40%);">+            for key, value in kwargs.items():</span><br><span>                 if value in getattr(test, key):</span><br><span>                     match.append(test)</span><br><span>                     continue</span><br><span>@@ -78,14 +78,14 @@</span><br><span>         return len(match)</span><br><span> </span><br><span>     def results_for(self, key):</span><br><span style="color: hsl(0, 100%, 40%);">-        print key.title() + ":"</span><br><span style="color: hsl(120, 100%, 40%);">+        print(key.title() + ":")</span><br><span>         things = self.unique(key)</span><br><span>         width = max(len(t) for t in things)</span><br><span>         results = [(self.occurances(**{key: t}), t) for t in things]</span><br><span>         results.sort(key=lambda tup: tup[0], reverse=True)</span><br><span>         for (count, name) in results:</span><br><span style="color: hsl(0, 100%, 40%);">-            print "\t%-*s %5d" % (width, name, count)</span><br><span style="color: hsl(0, 100%, 40%);">-        print ""</span><br><span style="color: hsl(120, 100%, 40%);">+            print("\t%-*s %5d" % (width, name, count))</span><br><span style="color: hsl(120, 100%, 40%);">+        print("")</span><br><span> </span><br><span> </span><br><span> def load_yaml_config(path):</span><br><span>@@ -95,10 +95,10 @@</span><br><span>     except IOError:</span><br><span>         # Ignore errors for the optional tests/custom folder.</span><br><span>         if path != "tests/custom/tests.yaml":</span><br><span style="color: hsl(0, 100%, 40%);">-            print "Failed to open %s" % path</span><br><span style="color: hsl(120, 100%, 40%);">+            print("Failed to open %s" % path)</span><br><span>         return None</span><br><span>     except:</span><br><span style="color: hsl(0, 100%, 40%);">-        print "Unexpected error: %s" % sys.exc_info()[0]</span><br><span style="color: hsl(120, 100%, 40%);">+        print("Unexpected error: %s" % sys.exc_info()[0])</span><br><span>         return None</span><br><span> </span><br><span>     config = yaml.load(f)</span><br><span>@@ -108,11 +108,11 @@</span><br><span> </span><br><span> </span><br><span> def main(argv=None):</span><br><span style="color: hsl(0, 100%, 40%);">-    print "Testsuite Module Usage and Coverage Report"</span><br><span style="color: hsl(0, 100%, 40%);">-    print ""</span><br><span style="color: hsl(120, 100%, 40%);">+    print("Testsuite Module Usage and Coverage Report")</span><br><span style="color: hsl(120, 100%, 40%);">+    print("")</span><br><span>     test_suite = TestSuite()</span><br><span style="color: hsl(0, 100%, 40%);">-    print "Number of tests:", len(test_suite.tests)</span><br><span style="color: hsl(0, 100%, 40%);">-    print ""</span><br><span style="color: hsl(120, 100%, 40%);">+    print("Number of tests:", len(test_suite.tests))</span><br><span style="color: hsl(120, 100%, 40%);">+    print("")</span><br><span>     test_suite.results_for('tags')</span><br><span>     test_suite.results_for('test_objects')</span><br><span>     test_suite.results_for('test_modules')</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/9315">change 9315</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/9315"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: testsuite </div>
<div style="display:none"> Gerrit-Branch: 13 </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: If76c2d3e11e4ab4552d0df7841287c8bb2de7918 </div>
<div style="display:none"> Gerrit-Change-Number: 9315 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Corey Farrell <git@cfware.com> </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins2 </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>