<p>Kevin Harwell has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/testsuite/+/18368">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Testsuite: fix several sporadically failing tests<br><br>This patches fixes problems across several tests:<br><br>A few of the 'pjsip/qualify' tests were failing due to a race between<br>the SIPp scenario, and the AMIEventModule both trying to stop the test.<br>This patch fixes the issue by ensuring the test is stopped only after<br>the expected AMI event is received.<br><br>The 'rest_api/applications/subscribe-endpoint/nominal/tech' test was<br>also failing due to a race condition between the order in which ARI<br>events were received. Under certain circumstances the event that<br>initiates the end of the test could be received before other required<br>events. This test was refactored to make it so 'stop_test' action does<br>does not trigger until all expected events are received.<br><br>The 'pjsip/subscriptions/presence/presencestate_repeat' test was failing<br>due to a race between initializing presence at startup and the SIPp<br>scenario subscribing. Depending on timing it was possible for the scenario<br>to start, and try to subscribe before the presence was initialized. When<br>this happened the test would fail. The solution was to guarantee the<br>scenario does not start until Asterisk has time to fully initialze its<br>state for the test. As such this patch adds a new 'SIPpActionModule' that<br>allows SIPp scenarios to now be triggered within an 'EventAction' setup.<br><br>Lastly, despite previous efforts to solve the problem some tests still<br>fail due to a 'port in use' error. Albeit, previous solutions have<br>lessened occurrences. The problem was ports in the testsuite were being<br>reserved based on the test's SIP listening port type, i.e UDP vs TCP.<br>However, the ports needing to be reserved are the media ports which are<br>all UDP based. This means TCP based tests were reserving TCP based media<br>ports, which can overlap. But the reservation of such ports essentially<br>have no meaning since SIPp attempts to bind using UDP. This patch ensures<br>reserving of all media ports now takes place using UDP only.<br><br>Change-Id: I5d8bb49ba8c8238ba76fd2545ff9be2ff0154399<br>---<br>M lib/python/asterisk/matcher_listener.py<br>M lib/python/asterisk/pluggable_modules.py<br>M lib/python/asterisk/self_test/test_utils_socket.py<br>M lib/python/asterisk/sipp.py<br>M lib/python/asterisk/utils_socket.py<br>M tests/channels/pjsip/qualify/basic/test-config.yaml<br>M tests/channels/pjsip/qualify/max_initial_qualify_time/test-config.yaml<br>M tests/channels/pjsip/qualify/qualify_timeout/test-config.yaml<br>M tests/channels/pjsip/subscriptions/presence/presencestate_repeat/configs/ast1/pjsip.conf<br>D tests/channels/pjsip/subscriptions/presence/presencestate_repeat/repeat_presence_state.py<br>M tests/channels/pjsip/subscriptions/presence/presencestate_repeat/test-config.yaml<br>M tests/rest_api/applications/subscribe-endpoint/nominal/tech/test-config.yaml<br>12 files changed, 214 insertions(+), 123 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/testsuite refs/changes/68/18368/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/lib/python/asterisk/matcher_listener.py b/lib/python/asterisk/matcher_listener.py</span><br><span>index cd2ca5a..69c2bf1 100644</span><br><span>--- a/lib/python/asterisk/matcher_listener.py</span><br><span>+++ b/lib/python/asterisk/matcher_listener.py</span><br><span>@@ -74,3 +74,20 @@</span><br><span> </span><br><span>         if not any(f for f in self.filter_msgs if re.match(f, msg)):</span><br><span>             self.check(msg)</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 Ari(PluggableConditions):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Pluggable module that that checks messages received over ARI"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, config, test_object, on_match=None):</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%);">+        Keyword Arguments:</span><br><span style="color: hsl(120, 100%, 40%);">+        config - configuration for this module</span><br><span style="color: hsl(120, 100%, 40%);">+        test_object - the TestCase driver</span><br><span style="color: hsl(120, 100%, 40%);">+        on_match - Optional callback called upon a conditional match</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%);">+        super(Ari, self).__init__(config, test_object, on_match)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        test_object.register_ws_event_handler(self.check)</span><br><span>diff --git a/lib/python/asterisk/pluggable_modules.py b/lib/python/asterisk/pluggable_modules.py</span><br><span>index 22da37e..c4b0d27 100644</span><br><span>--- a/lib/python/asterisk/pluggable_modules.py</span><br><span>+++ b/lib/python/asterisk/pluggable_modules.py</span><br><span>@@ -17,6 +17,7 @@</span><br><span> from twisted.internet import reactor</span><br><span> from starpy import fastagi</span><br><span> from .test_runner import load_and_parse_module</span><br><span style="color: hsl(120, 100%, 40%);">+from .sipp import SIPpActionModule, SIPpStartEventModule</span><br><span> from .pluggable_registry import PLUGGABLE_ACTION_REGISTRY,\</span><br><span>     PLUGGABLE_EVENT_REGISTRY,\</span><br><span>     PluggableRegistry</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 34672c6..b0bee5c 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>@@ -144,20 +144,20 @@</span><br><span>         p = 50000</span><br><span> </span><br><span>         def test(h, s, f):</span><br><span style="color: hsl(0, 100%, 40%);">-            self.assertEqual(get_available_port(h, s, f, 0, p), p)</span><br><span style="color: hsl(120, 100%, 40%);">+            self.assertEqual(get_available_port(h, p, s, f, 0), p)</span><br><span>         # Limit to single host since using global object</span><br><span>         self._on_socktype(None, test)</span><br><span> </span><br><span>         p = 50001</span><br><span> </span><br><span>         self.assertEqual(get_available_port(</span><br><span style="color: hsl(0, 100%, 40%);">-            config={'-i': '0.0.0.0'}, num=2, port=p), p)</span><br><span style="color: hsl(120, 100%, 40%);">+            '0.0.0.0', p, num=2,), p)</span><br><span>         self.assertEqual(get_available_port(</span><br><span style="color: hsl(0, 100%, 40%);">-            config={'-i': '0.0.0.0',  '-t': ''}, num=2, port=p), p)</span><br><span style="color: hsl(120, 100%, 40%);">+            '0.0.0.0', p, SOCK_STREAM, num=2), p)</span><br><span>         self.assertEqual(get_available_port(</span><br><span style="color: hsl(0, 100%, 40%);">-            config={'-i': '[::]'}, num=2, port=p), p)</span><br><span style="color: hsl(120, 100%, 40%);">+            '[::]', p, family=AF_INET6, num=2), p)</span><br><span>         self.assertEqual(get_available_port(</span><br><span style="color: hsl(0, 100%, 40%);">-            config={'-i': '[::]',  '-t': ''}, num=2, port=p), p)</span><br><span style="color: hsl(120, 100%, 40%);">+            '[::]', p, SOCK_STREAM, AF_INET6, 2), p)</span><br><span> </span><br><span> </span><br><span> if __name__ == "__main__":</span><br><span>diff --git a/lib/python/asterisk/sipp.py b/lib/python/asterisk/sipp.py</span><br><span>index 1341132..498bf19 100644</span><br><span>--- a/lib/python/asterisk/sipp.py</span><br><span>+++ b/lib/python/asterisk/sipp.py</span><br><span>@@ -17,7 +17,7 @@</span><br><span> from .test_case import TestCase</span><br><span> from .utils_socket import get_available_port</span><br><span> from .pluggable_registry import PLUGGABLE_EVENT_REGISTRY,\</span><br><span style="color: hsl(0, 100%, 40%);">-    PLUGGABLE_ACTION_REGISTRY, var_replace</span><br><span style="color: hsl(120, 100%, 40%);">+    PLUGGABLE_ACTION_REGISTRY</span><br><span> </span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span>@@ -686,7 +686,7 @@</span><br><span>             #</span><br><span>             # num = 4 = ports for audio rtp/rtcp and video rtp/rtcp</span><br><span>             default_args['-mp'] = str(get_available_port(</span><br><span style="color: hsl(0, 100%, 40%);">-                config=default_args, num=4))</span><br><span style="color: hsl(120, 100%, 40%);">+                default_args.get('-i'), num=4))</span><br><span> </span><br><span>         for (key, val) in default_args.items():</span><br><span>             sipp_args.extend([key, val])</span><br><span>@@ -962,3 +962,41 @@</span><br><span> </span><br><span> </span><br><span> PLUGGABLE_EVENT_REGISTRY.register("sipp-start", SIPpStartEventModule)</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 SIPpActionModule(object):</span><br><span style="color: hsl(120, 100%, 40%);">+    """An action module that initiates SIPp scenarios."""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, test_object, config):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Initialize SIPp action module"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        scenarios = config.get('scenarios')</span><br><span style="color: hsl(120, 100%, 40%);">+        if not scenarios:</span><br><span style="color: hsl(120, 100%, 40%);">+            # This is more of a fix the test error. Either this action</span><br><span style="color: hsl(120, 100%, 40%);">+            # is not needed, or a scenario needs to be properly added</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGGER.error("No registered SIPp scenarios (action does nothing).")</span><br><span style="color: hsl(120, 100%, 40%);">+            test_object.set_passed(False)</span><br><span style="color: hsl(120, 100%, 40%);">+            test_object.stop_reactor()</span><br><span style="color: hsl(120, 100%, 40%);">+            return</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        self.sequence = SIPpScenarioSequence(test_object,</span><br><span style="color: hsl(120, 100%, 40%);">+            fail_on_any=config.get('fail-on-any', True),</span><br><span style="color: hsl(120, 100%, 40%);">+            stop_on_done=config.get('stop-after-scenarios', True))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        for s in scenarios:</span><br><span style="color: hsl(120, 100%, 40%);">+            if ('coordinated-sender' in s and 'coordinated-receiver' in s):</span><br><span style="color: hsl(120, 100%, 40%);">+                self.sequence.register_scenario(CoordinatedScenario(</span><br><span style="color: hsl(120, 100%, 40%);">+                    test_object.test_name, s))</span><br><span style="color: hsl(120, 100%, 40%);">+            else:</span><br><span style="color: hsl(120, 100%, 40%);">+                self.sequence.register_scenario(SIPpScenario(</span><br><span style="color: hsl(120, 100%, 40%);">+                    test_object.test_name, s['key-args'],</span><br><span style="color: hsl(120, 100%, 40%);">+                    s.get('ordered-args') or [],</span><br><span style="color: hsl(120, 100%, 40%);">+                    s.get('target') or '127.0.0.1'))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def run(self, triggered_by, source, extra):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Execute specified SIPp scenarios"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        self.sequence.execute()</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%);">+PLUGGABLE_ACTION_REGISTRY.register("sipp", SIPpActionModule)</span><br><span>diff --git a/lib/python/asterisk/utils_socket.py b/lib/python/asterisk/utils_socket.py</span><br><span>index 158592d..c8813bd 100644</span><br><span>--- a/lib/python/asterisk/utils_socket.py</span><br><span>+++ b/lib/python/asterisk/utils_socket.py</span><br><span>@@ -39,6 +39,40 @@</span><br><span>     }.get(family, 'Unknown Family')</span><br><span> </span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+def get_resolved(host='', family=None):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Get and/or validate the family and host and/or resolve it"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def get_family(h, f):</span><br><span style="color: hsl(120, 100%, 40%);">+        try:</span><br><span style="color: hsl(120, 100%, 40%);">+            inet_pton(f, h)</span><br><span style="color: hsl(120, 100%, 40%);">+            return f</span><br><span style="color: hsl(120, 100%, 40%);">+        except:</span><br><span style="color: hsl(120, 100%, 40%);">+            return None</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if not host:</span><br><span style="color: hsl(120, 100%, 40%);">+        return ('', family or AF_INET)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    host = host.lstrip('[').rstrip(']')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if family == None:</span><br><span style="color: hsl(120, 100%, 40%);">+        family = get_family(host, AF_INET) or get_family(host, AF_INET6)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if family:</span><br><span style="color: hsl(120, 100%, 40%);">+            # host is already an ip address, so go ahead and return</span><br><span style="color: hsl(120, 100%, 40%);">+            return (host, family)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if family == None:</span><br><span style="color: hsl(120, 100%, 40%);">+        family = AF_INET</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    try:</span><br><span style="color: hsl(120, 100%, 40%);">+        host = getaddrinfo(host, None, family)[0][4][0]</span><br><span style="color: hsl(120, 100%, 40%);">+    except:</span><br><span style="color: hsl(120, 100%, 40%);">+        raise ValueError("Unable to resolve host '{0}' ({1}) ".format(</span><br><span style="color: hsl(120, 100%, 40%);">+            host, socket_family(family)))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return (host, family)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def get_unused_os_port(host='', port=0, socktype=SOCK_STREAM, family=AF_INET):</span><br><span>     """Retrieve an unused port from the OS.</span><br><span> </span><br><span>@@ -73,6 +107,9 @@</span><br><span>     res = 0</span><br><span>     s = socket(family, socktype)</span><br><span>     try:</span><br><span style="color: hsl(120, 100%, 40%);">+        if socktype == SOCK_STREAM:</span><br><span style="color: hsl(120, 100%, 40%);">+            s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>         s.bind((host, port))</span><br><span>         res = s.getsockname()[1]</span><br><span>     except error as e:</span><br><span>@@ -285,8 +322,8 @@</span><br><span> PORTS = Ports()</span><br><span> </span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-def get_available_port(host='', socktype=0, family=0, num=0,</span><br><span style="color: hsl(0, 100%, 40%);">-                       port=0, config=None):</span><br><span style="color: hsl(120, 100%, 40%);">+def get_available_port(host='', port=0, socktype=SOCK_DGRAM,</span><br><span style="color: hsl(120, 100%, 40%);">+                       family=None, num=0):</span><br><span>     """Retrieve the primary available port, and reserve it and its offsets.</span><br><span> </span><br><span>     The majority of use cases probably involve the need to reserve multiple</span><br><span>@@ -304,20 +341,8 @@</span><br><span>     The singular primary available port.</span><br><span>     """</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    if config:</span><br><span style="color: hsl(0, 100%, 40%);">-        host = host or config.get('-i')</span><br><span style="color: hsl(0, 100%, 40%);">-        socktype = SOCK_STREAM if '-t' in config else SOCK_DGRAM</span><br><span style="color: hsl(120, 100%, 40%);">+    host, family = get_resolved(host, family)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    # The family (without a host) and socktype should really always be</span><br><span style="color: hsl(0, 100%, 40%);">-    # specified when calling this function, but just in case use some defaults</span><br><span style="color: hsl(0, 100%, 40%);">-    if family < 1:</span><br><span style="color: hsl(0, 100%, 40%);">-        if host:</span><br><span style="color: hsl(0, 100%, 40%);">-            family = AF_INET6 if ':' in host else AF_INET</span><br><span style="color: hsl(0, 100%, 40%);">-        else:</span><br><span style="color: hsl(0, 100%, 40%);">-            family = AF_INET</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    if socktype < 1:</span><br><span style="color: hsl(0, 100%, 40%);">-        socktype = SOCK_DGRAM  # Most tests are UDP based</span><br><span>     available = PORTS.get_range_and_reserve(</span><br><span>         host, port, socktype, family, num)</span><br><span>     # If we don't have a valid socktype and family badness happens</span><br><span>diff --git a/tests/channels/pjsip/qualify/basic/test-config.yaml b/tests/channels/pjsip/qualify/basic/test-config.yaml</span><br><span>index 8e09b04..5be9ca5 100644</span><br><span>--- a/tests/channels/pjsip/qualify/basic/test-config.yaml</span><br><span>+++ b/tests/channels/pjsip/qualify/basic/test-config.yaml</span><br><span>@@ -11,11 +11,12 @@</span><br><span>     modules:</span><br><span>         -</span><br><span>             config-section: 'ami-config-13.5'</span><br><span style="color: hsl(0, 100%, 40%);">-            typename: 'ami.AMIEventModule'</span><br><span style="color: hsl(120, 100%, 40%);">+            typename: 'pluggable_modules.EventActionModule'</span><br><span> </span><br><span> test-object-config:</span><br><span>     fail-on-any: False</span><br><span>     reactor-timeout: 10</span><br><span style="color: hsl(120, 100%, 40%);">+    stop-after-scenarios: False</span><br><span>     test-iterations:</span><br><span>         -</span><br><span>             scenarios:</span><br><span>@@ -23,16 +24,18 @@</span><br><span> </span><br><span> ami-config-13.5:</span><br><span>     -</span><br><span style="color: hsl(0, 100%, 40%);">-        type: 'headermatch'</span><br><span style="color: hsl(0, 100%, 40%);">-        id: '0'</span><br><span style="color: hsl(0, 100%, 40%);">-        conditions:</span><br><span style="color: hsl(0, 100%, 40%);">-            match:</span><br><span style="color: hsl(0, 100%, 40%);">-                Event: 'ContactStatus'</span><br><span style="color: hsl(0, 100%, 40%);">-                ContactStatus: 'Reachable'</span><br><span style="color: hsl(0, 100%, 40%);">-        requirements:</span><br><span style="color: hsl(0, 100%, 40%);">-            match:</span><br><span style="color: hsl(0, 100%, 40%);">-                URI: 'sip:127.0.0.1:5061'</span><br><span style="color: hsl(0, 100%, 40%);">-        count: '>1'</span><br><span style="color: hsl(120, 100%, 40%);">+        ami-start:</span><br><span style="color: hsl(120, 100%, 40%);">+    -</span><br><span style="color: hsl(120, 100%, 40%);">+        ami-events:</span><br><span style="color: hsl(120, 100%, 40%);">+            conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+                match:</span><br><span style="color: hsl(120, 100%, 40%);">+                    Event: 'ContactStatus'</span><br><span style="color: hsl(120, 100%, 40%);">+                    ContactStatus: 'Reachable'</span><br><span style="color: hsl(120, 100%, 40%);">+            requirements:</span><br><span style="color: hsl(120, 100%, 40%);">+                match:</span><br><span style="color: hsl(120, 100%, 40%);">+                    URI: 'sip:127.0.0.1:5061'</span><br><span style="color: hsl(120, 100%, 40%);">+            count: '>1'</span><br><span style="color: hsl(120, 100%, 40%);">+        stop_test:</span><br><span> </span><br><span> properties:</span><br><span>     dependencies:</span><br><span>diff --git a/tests/channels/pjsip/qualify/max_initial_qualify_time/test-config.yaml b/tests/channels/pjsip/qualify/max_initial_qualify_time/test-config.yaml</span><br><span>index 06e43af..99bae87 100644</span><br><span>--- a/tests/channels/pjsip/qualify/max_initial_qualify_time/test-config.yaml</span><br><span>+++ b/tests/channels/pjsip/qualify/max_initial_qualify_time/test-config.yaml</span><br><span>@@ -18,6 +18,7 @@</span><br><span>     connect-ami: True</span><br><span>     fail-on-any: False</span><br><span>     reactor-timeout: 15</span><br><span style="color: hsl(120, 100%, 40%);">+    stop-after-scenarios: False</span><br><span>     test-iterations:</span><br><span>         -</span><br><span>             scenarios:</span><br><span>diff --git a/tests/channels/pjsip/qualify/qualify_timeout/test-config.yaml b/tests/channels/pjsip/qualify/qualify_timeout/test-config.yaml</span><br><span>index ad4fb55..6f97f4f 100644</span><br><span>--- a/tests/channels/pjsip/qualify/qualify_timeout/test-config.yaml</span><br><span>+++ b/tests/channels/pjsip/qualify/qualify_timeout/test-config.yaml</span><br><span>@@ -11,11 +11,12 @@</span><br><span>     modules:</span><br><span>         -</span><br><span>             config-section: 'ami-config-13.5'</span><br><span style="color: hsl(0, 100%, 40%);">-            typename: 'ami.AMIEventModule'</span><br><span style="color: hsl(120, 100%, 40%);">+            typename: 'pluggable_modules.EventActionModule'</span><br><span> </span><br><span> test-object-config:</span><br><span>     fail-on-any: False</span><br><span>     reactor-timeout: 25</span><br><span style="color: hsl(120, 100%, 40%);">+    stop-after-scenarios: False</span><br><span>     test-iterations:</span><br><span>         -</span><br><span>             scenarios:</span><br><span>@@ -23,16 +24,18 @@</span><br><span> </span><br><span> ami-config-13.5:</span><br><span>     -</span><br><span style="color: hsl(0, 100%, 40%);">-        type: 'headermatch'</span><br><span style="color: hsl(0, 100%, 40%);">-        id: '0'</span><br><span style="color: hsl(0, 100%, 40%);">-        conditions:</span><br><span style="color: hsl(0, 100%, 40%);">-            match:</span><br><span style="color: hsl(0, 100%, 40%);">-                Event: 'ContactStatus'</span><br><span style="color: hsl(0, 100%, 40%);">-                ContactStatus: 'Unreachable'</span><br><span style="color: hsl(0, 100%, 40%);">-        requirements:</span><br><span style="color: hsl(0, 100%, 40%);">-            match:</span><br><span style="color: hsl(0, 100%, 40%);">-                URI: 'sip:127.0.0.1:5061'</span><br><span style="color: hsl(0, 100%, 40%);">-        count: '>1'</span><br><span style="color: hsl(120, 100%, 40%);">+        ami-start:</span><br><span style="color: hsl(120, 100%, 40%);">+    -</span><br><span style="color: hsl(120, 100%, 40%);">+        ami-events:</span><br><span style="color: hsl(120, 100%, 40%);">+            conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+                match:</span><br><span style="color: hsl(120, 100%, 40%);">+                    Event: 'ContactStatus'</span><br><span style="color: hsl(120, 100%, 40%);">+                    ContactStatus: 'Unreachable'</span><br><span style="color: hsl(120, 100%, 40%);">+            requirements:</span><br><span style="color: hsl(120, 100%, 40%);">+                match:</span><br><span style="color: hsl(120, 100%, 40%);">+                    URI: 'sip:127.0.0.1:5061'</span><br><span style="color: hsl(120, 100%, 40%);">+            count: '>1'</span><br><span style="color: hsl(120, 100%, 40%);">+        stop_test:</span><br><span> </span><br><span> properties:</span><br><span>     dependencies:</span><br><span>diff --git a/tests/channels/pjsip/subscriptions/presence/presencestate_repeat/configs/ast1/pjsip.conf b/tests/channels/pjsip/subscriptions/presence/presencestate_repeat/configs/ast1/pjsip.conf</span><br><span>index 3e4adca..6c21810 100644</span><br><span>--- a/tests/channels/pjsip/subscriptions/presence/presencestate_repeat/configs/ast1/pjsip.conf</span><br><span>+++ b/tests/channels/pjsip/subscriptions/presence/presencestate_repeat/configs/ast1/pjsip.conf</span><br><span>@@ -1,3 +1,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">+[global]</span><br><span style="color: hsl(120, 100%, 40%);">+type=global</span><br><span style="color: hsl(120, 100%, 40%);">+debug=yes</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> [main-transport]</span><br><span> type=transport</span><br><span> bind = 127.0.0.1</span><br><span>diff --git a/tests/channels/pjsip/subscriptions/presence/presencestate_repeat/repeat_presence_state.py b/tests/channels/pjsip/subscriptions/presence/presencestate_repeat/repeat_presence_state.py</span><br><span>deleted file mode 100644</span><br><span>index 161ad5c..0000000</span><br><span>--- a/tests/channels/pjsip/subscriptions/presence/presencestate_repeat/repeat_presence_state.py</span><br><span>+++ /dev/null</span><br><span>@@ -1,29 +0,0 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python</span><br><span style="color: hsl(0, 100%, 40%);">-'''</span><br><span style="color: hsl(0, 100%, 40%);">-Copyright (C) 2014, Digium, Inc.</span><br><span style="color: hsl(0, 100%, 40%);">-Mark Michelson <mmichelson@digium.com></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-This program is free software, distributed under the terms of</span><br><span style="color: hsl(0, 100%, 40%);">-the GNU General Public License Version 2.</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%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class RepeatPresenceState(object):</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self, module_config, test_object):</span><br><span style="color: hsl(0, 100%, 40%);">-        self.ami = None</span><br><span style="color: hsl(0, 100%, 40%);">-        self.ami_message = {</span><br><span style="color: hsl(0, 100%, 40%);">-            'Action': 'SetVar',</span><br><span style="color: hsl(0, 100%, 40%);">-            'Variable': 'PRESENCE_STATE(CustomPresence:Eggs)',</span><br><span style="color: hsl(0, 100%, 40%);">-            'Value': 'AWAY,scrambled,feeling a bit sick'</span><br><span style="color: hsl(0, 100%, 40%);">-        }</span><br><span style="color: hsl(0, 100%, 40%);">-        test_object.register_ami_observer(self.ami_connect)</span><br><span style="color: hsl(0, 100%, 40%);">-        test_object.register_scenario_started_observer(self.scenario_started)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def ami_connect(self, ami):</span><br><span style="color: hsl(0, 100%, 40%);">-        self.ami = ami</span><br><span style="color: hsl(0, 100%, 40%);">-        # Give ourselves an initial presence</span><br><span style="color: hsl(0, 100%, 40%);">-        self.ami.sendMessage(self.ami_message)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def scenario_started(self, scenario):</span><br><span style="color: hsl(0, 100%, 40%);">-        # Now repeat the same presence state.</span><br><span style="color: hsl(0, 100%, 40%);">-        self.ami.sendMessage(self.ami_message)</span><br><span>diff --git a/tests/channels/pjsip/subscriptions/presence/presencestate_repeat/test-config.yaml b/tests/channels/pjsip/subscriptions/presence/presencestate_repeat/test-config.yaml</span><br><span>index 0bc6781..bb363d7 100644</span><br><span>--- a/tests/channels/pjsip/subscriptions/presence/presencestate_repeat/test-config.yaml</span><br><span>+++ b/tests/channels/pjsip/subscriptions/presence/presencestate_repeat/test-config.yaml</span><br><span>@@ -17,18 +17,56 @@</span><br><span>         - refleaks</span><br><span> </span><br><span> test-modules:</span><br><span style="color: hsl(0, 100%, 40%);">-    add-test-to-search-path: 'True'</span><br><span>     test-object:</span><br><span style="color: hsl(0, 100%, 40%);">-        config-section: sipp-config</span><br><span style="color: hsl(0, 100%, 40%);">-        typename: 'sipp.SIPpTestCase'</span><br><span style="color: hsl(120, 100%, 40%);">+        config-section: object-config</span><br><span style="color: hsl(120, 100%, 40%);">+        typename: test_case.TestCaseModule</span><br><span>     modules:</span><br><span>         -</span><br><span style="color: hsl(0, 100%, 40%);">-            typename: 'repeat_presence_state.RepeatPresenceState'</span><br><span style="color: hsl(120, 100%, 40%);">+            config-section: event-action-config</span><br><span style="color: hsl(120, 100%, 40%);">+            typename: pluggable_modules.EventActionModule</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-sipp-config:</span><br><span style="color: hsl(0, 100%, 40%);">-    reactor-timeout: 30</span><br><span style="color: hsl(0, 100%, 40%);">-    fail-on-any: True</span><br><span style="color: hsl(0, 100%, 40%);">-    test-iterations:</span><br><span style="color: hsl(0, 100%, 40%);">-        -</span><br><span style="color: hsl(120, 100%, 40%);">+object-config:</span><br><span style="color: hsl(120, 100%, 40%);">+    reactor-timeout: 15</span><br><span style="color: hsl(120, 100%, 40%);">+    connect-ami: True</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+event-action-config:</span><br><span style="color: hsl(120, 100%, 40%);">+    -</span><br><span style="color: hsl(120, 100%, 40%);">+        # On startup set the status</span><br><span style="color: hsl(120, 100%, 40%);">+        ami-start:</span><br><span style="color: hsl(120, 100%, 40%);">+        ami-actions:</span><br><span style="color: hsl(120, 100%, 40%);">+            action:</span><br><span style="color: hsl(120, 100%, 40%);">+                Action: SetVar</span><br><span style="color: hsl(120, 100%, 40%);">+                Variable: PRESENCE_STATE(CustomPresence:Eggs)</span><br><span style="color: hsl(120, 100%, 40%);">+                Value: AWAY,scrambled,feeling a bit sick</span><br><span style="color: hsl(120, 100%, 40%);">+    -</span><br><span style="color: hsl(120, 100%, 40%);">+        # Once the status is set issue a subscribe from the endpoint</span><br><span style="color: hsl(120, 100%, 40%);">+        ami-events:</span><br><span style="color: hsl(120, 100%, 40%);">+            conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+                match:</span><br><span style="color: hsl(120, 100%, 40%);">+                    Event: PresenceStatus</span><br><span style="color: hsl(120, 100%, 40%);">+            requirements:</span><br><span style="color: hsl(120, 100%, 40%);">+                match:</span><br><span style="color: hsl(120, 100%, 40%);">+                    Status: away</span><br><span style="color: hsl(120, 100%, 40%);">+                    Subtype: scrambled</span><br><span style="color: hsl(120, 100%, 40%);">+                    Message: feeling a bit sick</span><br><span style="color: hsl(120, 100%, 40%);">+            count: '1'</span><br><span style="color: hsl(120, 100%, 40%);">+        sipp:</span><br><span>             scenarios:</span><br><span>                 - { 'key-args': {'scenario': 'subscribe.xml', '-p': '5061'} }</span><br><span style="color: hsl(120, 100%, 40%);">+    -</span><br><span style="color: hsl(120, 100%, 40%);">+        # The test will end on completion of the SIPp scenario, but this event</span><br><span style="color: hsl(120, 100%, 40%);">+        # and subsequent action should trigger before that occurs</span><br><span style="color: hsl(120, 100%, 40%);">+        ami-events:</span><br><span style="color: hsl(120, 100%, 40%);">+            conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+                match:</span><br><span style="color: hsl(120, 100%, 40%);">+                    Event: TestEvent</span><br><span style="color: hsl(120, 100%, 40%);">+                    State: SUBSCRIPTION_STATE_SET</span><br><span style="color: hsl(120, 100%, 40%);">+            requirements:</span><br><span style="color: hsl(120, 100%, 40%);">+                match:</span><br><span style="color: hsl(120, 100%, 40%);">+                    StateText: ACTIVE</span><br><span style="color: hsl(120, 100%, 40%);">+            count: '1'</span><br><span style="color: hsl(120, 100%, 40%);">+        ami-actions:</span><br><span style="color: hsl(120, 100%, 40%);">+            action:</span><br><span style="color: hsl(120, 100%, 40%);">+                Action: SetVar</span><br><span style="color: hsl(120, 100%, 40%);">+                Variable: PRESENCE_STATE(CustomPresence:Eggs)</span><br><span style="color: hsl(120, 100%, 40%);">+                Value: AWAY,scrambled,feeling a bit sick</span><br><span>diff --git a/tests/rest_api/applications/subscribe-endpoint/nominal/tech/test-config.yaml b/tests/rest_api/applications/subscribe-endpoint/nominal/tech/test-config.yaml</span><br><span>index e8b2976..b677f7d 100644</span><br><span>--- a/tests/rest_api/applications/subscribe-endpoint/nominal/tech/test-config.yaml</span><br><span>+++ b/tests/rest_api/applications/subscribe-endpoint/nominal/tech/test-config.yaml</span><br><span>@@ -28,10 +28,7 @@</span><br><span>             config-section: subscriber</span><br><span>             typename: 'subscriber.ResourceSubscription'</span><br><span>         -</span><br><span style="color: hsl(0, 100%, 40%);">-            config-section: ari-config</span><br><span style="color: hsl(0, 100%, 40%);">-            typename: ari.WebSocketEventModule</span><br><span style="color: hsl(0, 100%, 40%);">-        -</span><br><span style="color: hsl(0, 100%, 40%);">-            config-section: ari-test-stopper</span><br><span style="color: hsl(120, 100%, 40%);">+            config-section: event-action-config</span><br><span>             typename: pluggable_modules.EventActionModule</span><br><span> </span><br><span> test-object-config:</span><br><span>@@ -58,19 +55,11 @@</span><br><span>     subscriptions:</span><br><span>         - { event-source: 'endpoint:IAX2', app: 'testsuite' }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-ari-config:</span><br><span style="color: hsl(0, 100%, 40%);">-    events:</span><br><span style="color: hsl(0, 100%, 40%);">-        -   conditions:</span><br><span style="color: hsl(0, 100%, 40%);">-                match:</span><br><span style="color: hsl(0, 100%, 40%);">-                    type: EndpointStateChange</span><br><span style="color: hsl(0, 100%, 40%);">-                    application: testsuite</span><br><span style="color: hsl(0, 100%, 40%);">-                    endpoint:</span><br><span style="color: hsl(0, 100%, 40%);">-                        technology: IAX2</span><br><span style="color: hsl(0, 100%, 40%);">-                        resource: alice</span><br><span style="color: hsl(0, 100%, 40%);">-                        state: unknown</span><br><span style="color: hsl(0, 100%, 40%);">-                        channel_ids: ['.*']</span><br><span style="color: hsl(0, 100%, 40%);">-            count: 1</span><br><span style="color: hsl(0, 100%, 40%);">-        -   conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+event-action-config:</span><br><span style="color: hsl(120, 100%, 40%);">+    event:</span><br><span style="color: hsl(120, 100%, 40%);">+        type: 'matcher_listener.Ari'</span><br><span style="color: hsl(120, 100%, 40%);">+        conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+            -</span><br><span>                 match:</span><br><span>                     type: EndpointStateChange</span><br><span>                     application: testsuite</span><br><span>@@ -79,8 +68,17 @@</span><br><span>                         resource: bob</span><br><span>                         state: online</span><br><span>                         channel_ids: ['.*']</span><br><span style="color: hsl(0, 100%, 40%);">-            count: 0</span><br><span style="color: hsl(0, 100%, 40%);">-        -   conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+                count: 0</span><br><span style="color: hsl(120, 100%, 40%);">+            -</span><br><span style="color: hsl(120, 100%, 40%);">+                match:</span><br><span style="color: hsl(120, 100%, 40%);">+                    type: EndpointStateChange</span><br><span style="color: hsl(120, 100%, 40%);">+                    application: testsuite</span><br><span style="color: hsl(120, 100%, 40%);">+                    endpoint:</span><br><span style="color: hsl(120, 100%, 40%);">+                        technology: IAX2</span><br><span style="color: hsl(120, 100%, 40%);">+                        resource: alice</span><br><span style="color: hsl(120, 100%, 40%);">+                        state: unknown</span><br><span style="color: hsl(120, 100%, 40%);">+                        channel_ids: ['.*']</span><br><span style="color: hsl(120, 100%, 40%);">+            -</span><br><span>                 match:</span><br><span>                     type: ChannelCreated</span><br><span>                     application: testsuite</span><br><span>@@ -88,32 +86,28 @@</span><br><span>                         name: 'IAX2/alice-.*'</span><br><span>                         state: Down</span><br><span>                         dialplan: { context: 'default', exten: 's', priority: 1 }</span><br><span style="color: hsl(0, 100%, 40%);">-            count: 1</span><br><span style="color: hsl(0, 100%, 40%);">-        -   conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+            -</span><br><span>                 match:</span><br><span>                     type: ChannelStateChange</span><br><span>                     application: testsuite</span><br><span>                     channel:</span><br><span>                         name: 'IAX2/alice-.*'</span><br><span>                         state: Ringing</span><br><span style="color: hsl(0, 100%, 40%);">-            count: 1</span><br><span style="color: hsl(0, 100%, 40%);">-        -   conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+            -</span><br><span>                 match:</span><br><span>                     type: ChannelStateChange</span><br><span>                     application: testsuite</span><br><span>                     channel:</span><br><span>                         name: 'IAX2/alice-.*'</span><br><span>                         state: Up</span><br><span style="color: hsl(0, 100%, 40%);">-            count: 1</span><br><span style="color: hsl(0, 100%, 40%);">-        -   conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+            -</span><br><span>                 match:</span><br><span>                     type: ChannelHangupRequest</span><br><span>                     application: testsuite</span><br><span>                     channel:</span><br><span>                         name: 'IAX2/alice-.*'</span><br><span>                         state: Up</span><br><span style="color: hsl(0, 100%, 40%);">-            count: 1</span><br><span style="color: hsl(0, 100%, 40%);">-        -   conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+            -</span><br><span>                 match:</span><br><span>                     type: ChannelDestroyed</span><br><span>                     application: testsuite</span><br><span>@@ -122,20 +116,16 @@</span><br><span>                     channel:</span><br><span>                         name: 'IAX2/alice-.*'</span><br><span>                         state: Up</span><br><span style="color: hsl(0, 100%, 40%);">-            count: 1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-ari-test-stopper:</span><br><span style="color: hsl(0, 100%, 40%);">-    -</span><br><span style="color: hsl(0, 100%, 40%);">-        ari-events:</span><br><span style="color: hsl(0, 100%, 40%);">-            match:</span><br><span style="color: hsl(0, 100%, 40%);">-                type: EndpointStateChange</span><br><span style="color: hsl(0, 100%, 40%);">-                application: testsuite</span><br><span style="color: hsl(0, 100%, 40%);">-                endpoint:</span><br><span style="color: hsl(0, 100%, 40%);">-                    technology: IAX2</span><br><span style="color: hsl(0, 100%, 40%);">-                    resource: alice</span><br><span style="color: hsl(0, 100%, 40%);">-                    state: unknown</span><br><span style="color: hsl(0, 100%, 40%);">-                    channel_ids: []</span><br><span style="color: hsl(0, 100%, 40%);">-        stop_test:</span><br><span style="color: hsl(120, 100%, 40%);">+            -</span><br><span style="color: hsl(120, 100%, 40%);">+                match:</span><br><span style="color: hsl(120, 100%, 40%);">+                    type: EndpointStateChange</span><br><span style="color: hsl(120, 100%, 40%);">+                    application: testsuite</span><br><span style="color: hsl(120, 100%, 40%);">+                    endpoint:</span><br><span style="color: hsl(120, 100%, 40%);">+                        technology: IAX2</span><br><span style="color: hsl(120, 100%, 40%);">+                        resource: alice</span><br><span style="color: hsl(120, 100%, 40%);">+                        state: unknown</span><br><span style="color: hsl(120, 100%, 40%);">+                        channel_ids: []</span><br><span style="color: hsl(120, 100%, 40%);">+    stop_test:</span><br><span> </span><br><span> properties:</span><br><span>     dependencies:</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/testsuite/+/18368">change 18368</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/c/testsuite/+/18368"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: testsuite </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I5d8bb49ba8c8238ba76fd2545ff9be2ff0154399 </div>
<div style="display:none"> Gerrit-Change-Number: 18368 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>