<p>Friendly Automation <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/testsuite/+/19520">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span></span><br></pre><div style="white-space:pre-wrap">Approvals:
  Joshua Colp: Looks good to me, but someone else must approve
  George Joseph: Looks good to me, approved
  Friendly Automation: Approved for Submit

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">sipp_iterator: converting iterator into pluggable module<br><br>Per ASTERISK~30214, repeating scenario tests required a mechanism<br>to force scenario/ami action timing.  This change renames the<br>scenario_iterator sipp_iterator and makes it a pluggable module<br>that can read normal test yaml files.<br><br>The two existing tests that used the iterator have been moved to<br>the new method, but further work is required to migrate other<br>tests with this issue<br><br>ASTERISK_30271 #noclose<br><br>Change-Id: I57ed85d2a2a044207da23a750e433dcc38eb93ff<br>---<br>M lib/python/asterisk/pluggable_modules.py<br>D lib/python/asterisk/scenario_iterator.py<br>A lib/python/asterisk/sipp_iterator.py<br>D tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/mwi_check.py<br>M tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/test-config.yaml<br>D tests/channels/pjsip/subscriptions/mwi/unsolicited/mailbox_count_changes/mwi_check.py<br>M tests/channels/pjsip/subscriptions/mwi/unsolicited/mailbox_count_changes/test-config.yaml<br>7 files changed, 439 insertions(+), 339 deletions(-)<br><br></pre>
<pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/lib/python/asterisk/pluggable_modules.py b/lib/python/asterisk/pluggable_modules.py</span><br><span>index d143da7..4f757e0 100644</span><br><span>--- a/lib/python/asterisk/pluggable_modules.py</span><br><span>+++ b/lib/python/asterisk/pluggable_modules.py</span><br><span>@@ -18,6 +18,7 @@</span><br><span> from starpy import fastagi</span><br><span> from .test_runner import load_and_parse_module</span><br><span> from .sipp import SIPpActionModule, SIPpStartEventModule</span><br><span style="color: hsl(120, 100%, 40%);">+from .sipp_iterator import SIPpIteratorActionModule</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/scenario_iterator.py b/lib/python/asterisk/scenario_iterator.py</span><br><span>deleted file mode 100644</span><br><span>index cff1af3..0000000</span><br><span>--- a/lib/python/asterisk/scenario_iterator.py</span><br><span>+++ /dev/null</span><br><span>@@ -1,231 +0,0 @@</span><br><span style="color: hsl(0, 100%, 40%);">-'''</span><br><span style="color: hsl(0, 100%, 40%);">-Copyright (C) 2022, Sangoma Technologies Corp</span><br><span style="color: hsl(0, 100%, 40%);">-Mike Bradeen <mbradeen@sangoma.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%);">-The scenario_iterator is designed to let us start sipp, then perform an ami</span><br><span style="color: hsl(0, 100%, 40%);">-action as a cycle of start sipp->generate event(s)->sipp stops, start sipp...</span><br><span style="color: hsl(0, 100%, 40%);">-This is for tests that are performing the same test different ways or performing</span><br><span style="color: hsl(0, 100%, 40%);">-different iterations of the same test.</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-An example would be to start a scenario that waits for a NOTIFY, then send the AMI</span><br><span style="color: hsl(0, 100%, 40%);">-to generate that NOTIFY, then starts a second scenario, send a second AMI, etc.</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-There are two classes for this, the singleIterator and the multiIterator.  The</span><br><span style="color: hsl(0, 100%, 40%);">-singleIterator is one-to-one, with each scenario start triggering the next ami and the</span><br><span style="color: hsl(0, 100%, 40%);">-scenario stop triggering the next scenario start.  It is fed a pair of lists, one is a</span><br><span style="color: hsl(0, 100%, 40%);">-list of scenarios and the other is a list of AMI actions.  After the final scenario,</span><br><span style="color: hsl(0, 100%, 40%);">-we add a 'done' indicator to the sipp side and a final event on the AMI side, example:</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-scenarios = [</span><br><span style="color: hsl(0, 100%, 40%);">-    {'Name': 'none'},</span><br><span style="color: hsl(0, 100%, 40%);">-    {'Name': 'alice-is-notified-1.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(0, 100%, 40%);">-    {'Name': 'alice-is-notified-2.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(0, 100%, 40%);">-    {'Name': 'done'}</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%);">-actions = [</span><br><span style="color: hsl(0, 100%, 40%);">-    {'Action': 'UserEvent', 'UserEvent': 'testStarted'}</span><br><span style="color: hsl(0, 100%, 40%);">-    {'Action': 'MWIUpdate', 'Mailbox': 'alice', 'NewMessages':'2', 'OldMessages':'0'},</span><br><span style="color: hsl(0, 100%, 40%);">-    {'Action': 'MWIDelete', 'Mailbox': 'alice'},</span><br><span style="color: hsl(0, 100%, 40%);">-    {'Action': 'UserEvent', 'UserEvent': 'testComplete'}</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%);">-You then define a local instance and run it:</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-def start_test(test_object, junk):</span><br><span style="color: hsl(0, 100%, 40%);">-    testrunner = singleIterator(test_object, scenarios, actions)</span><br><span style="color: hsl(0, 100%, 40%);">-    testrunner.run(junk)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-The multiIterator is a many-to-many mapper that allows multiple scenarios to be started, followed</span><br><span style="color: hsl(0, 100%, 40%);">-by multiple actions.  The end of the last scenario in the list causes the next scenario to start.</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-Multiple sequences can be tied to individual actions, or vice versa. In this example we start two</span><br><span style="color: hsl(0, 100%, 40%);">-scenarios but then only generate one corresponding AMI:</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-scenarios = [</span><br><span style="color: hsl(0, 100%, 40%);">-        {'Name': 'mailbox_a', 'sequence': [</span><br><span style="color: hsl(0, 100%, 40%);">-            {'Name': 'alice-is-notified-1.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(0, 100%, 40%);">-            {'Name': 'bob-is-notified-1.xml', 'port': '5062', 'target': '127.0.0.1'} ]},</span><br><span style="color: hsl(0, 100%, 40%);">-        {'Name': 'done'}</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%);">-actions = [</span><br><span style="color: hsl(0, 100%, 40%);">-        {'Messages': [{'Action': 'MWIUpdate', 'Mailbox': 'mailbox_a', 'NewMessages':'2', 'OldMessages':'1'}]},</span><br><span style="color: hsl(0, 100%, 40%);">-        {'Messages': [{'Action': 'UserEvent', 'UserEvent': 'testComplete'}]}</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%);">-In the above examples, we end the test with a testComplete event that we then use to trigger the</span><br><span style="color: hsl(0, 100%, 40%);">-test stop event in the corresponding yaml file, example:</span><br><span style="color: hsl(0, 100%, 40%);">-ami-config:</span><br><span style="color: hsl(0, 100%, 40%);">-    -</span><br><span style="color: hsl(0, 100%, 40%);">-        ami-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%);">-                    Event: 'UserEvent'</span><br><span style="color: hsl(0, 100%, 40%);">-                    UserEvent: 'testComplete'</span><br><span style="color: hsl(0, 100%, 40%);">-            count: 1</span><br><span style="color: hsl(0, 100%, 40%);">-        stop_test:</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-Also, this test REQUIRES the following be set in the sipp configuration section of your yaml file if</span><br><span style="color: hsl(0, 100%, 40%);">-you are using a sipp scenario (like a REGISTER) to kick off the sequnce:</span><br><span style="color: hsl(0, 100%, 40%);">-    stop-after-scenarios: False</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-A Name of 'none' for a scenario(list) or an Action of 'none' skips that particular scenario or action,</span><br><span style="color: hsl(0, 100%, 40%);">-but not the other. This is mostly to allow one or more AMI events to be triggered before the</span><br><span style="color: hsl(0, 100%, 40%);">-corresponding sipp scenario is started but could also allow for intermediate sipp scenarios. Setting</span><br><span style="color: hsl(0, 100%, 40%);">-both to 'none' would be functionally the same as waiting for 1 second before going on to the next</span><br><span style="color: hsl(0, 100%, 40%);">-iteration.</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%);">-from asterisk.sipp import SIPpScenarioSequence</span><br><span style="color: hsl(0, 100%, 40%);">-from asterisk.sipp import SIPpScenario</span><br><span style="color: hsl(0, 100%, 40%);">-from twisted.internet import reactor</span><br><span style="color: hsl(0, 100%, 40%);">-import sys</span><br><span style="color: hsl(0, 100%, 40%);">-import logging</span><br><span style="color: hsl(0, 100%, 40%);">-</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%);">-LOGGER = logging.getLogger(__name__)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-sipp_terminator = {'Name': 'done'}</span><br><span style="color: hsl(0, 100%, 40%);">-empty_action = {'Action': 'none'}</span><br><span style="color: hsl(0, 100%, 40%);">-empty_action_list = {'Messages': [{'Action': 'none'}] }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class singleIterator(object):</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self, test_object, scenarios, actions):</span><br><span style="color: hsl(0, 100%, 40%);">-        self.test_object = test_object</span><br><span style="color: hsl(0, 100%, 40%);">-        self.scenarios = scenarios</span><br><span style="color: hsl(0, 100%, 40%);">-        self.actions = actions</span><br><span style="color: hsl(0, 100%, 40%);">-        self.iteration = 0</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __iterate(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        try:</span><br><span style="color: hsl(0, 100%, 40%);">-            scenario = self.scenarios[self.iteration]</span><br><span style="color: hsl(0, 100%, 40%);">-        except IndexError:</span><br><span style="color: hsl(0, 100%, 40%);">-            LOGGER.warning("End of scenario list without proper termination")</span><br><span style="color: hsl(0, 100%, 40%);">-            scenario = sipp_terminator</span><br><span style="color: hsl(0, 100%, 40%);">-        try:</span><br><span style="color: hsl(0, 100%, 40%);">-            message = self.actions[self.iteration]</span><br><span style="color: hsl(0, 100%, 40%);">-        except IndexError:</span><br><span style="color: hsl(0, 100%, 40%);">-            LOGGER.warning("End of action list without proper termination")</span><br><span style="color: hsl(0, 100%, 40%);">-            message = empty_action</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        self.iteration += 1</span><br><span style="color: hsl(0, 100%, 40%);">-        if scenario['Name'] == 'none':</span><br><span style="color: hsl(0, 100%, 40%);">-            # skip ahead to the next iteration but send the AMI</span><br><span style="color: hsl(0, 100%, 40%);">-            # action if set. Speed up the iteration.</span><br><span style="color: hsl(0, 100%, 40%);">-            self.__sendMessage(message, 0)</span><br><span style="color: hsl(0, 100%, 40%);">-            reactor.callLater(1, self.run)</span><br><span style="color: hsl(0, 100%, 40%);">-        elif scenario['Name'] != 'done':</span><br><span style="color: hsl(0, 100%, 40%);">-            # A scenaro was specified so run it then schedule the</span><br><span style="color: hsl(0, 100%, 40%);">-            # AMI event if there is one.</span><br><span style="color: hsl(0, 100%, 40%);">-            self.__startScenario(scenario)</span><br><span style="color: hsl(0, 100%, 40%);">-            self.__sendMessage(message)</span><br><span style="color: hsl(0, 100%, 40%);">-        else:</span><br><span style="color: hsl(0, 100%, 40%);">-            # At the final iteration, send any final AMI immediately</span><br><span style="color: hsl(0, 100%, 40%);">-            self.__sendMessage(message, 0)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __startScenario(self, scenario):</span><br><span style="color: hsl(0, 100%, 40%);">-        LOGGER.info("Starting sipp scenario %s" % scenario['Name'])</span><br><span style="color: hsl(0, 100%, 40%);">-        sipp_scenario = SIPpScenario(self.test_object.test_name,</span><br><span style="color: hsl(0, 100%, 40%);">-                                     {'scenario': scenario['Name'],</span><br><span style="color: hsl(0, 100%, 40%);">-                                      '-p': scenario['port']},</span><br><span style="color: hsl(0, 100%, 40%);">-                                     target=scenario['target'])</span><br><span style="color: hsl(0, 100%, 40%);">-        exiter = sipp_scenario.run(self.test_object)</span><br><span style="color: hsl(0, 100%, 40%);">-        exiter.addCallback(self.run)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __sendMessage(self, message, delay=2):</span><br><span style="color: hsl(0, 100%, 40%);">-        if message['Action'] != 'none':</span><br><span style="color: hsl(0, 100%, 40%);">-            testami = self.test_object.ami[0]</span><br><span style="color: hsl(0, 100%, 40%);">-            LOGGER.info("Scheduling AMI %s" % message['Action'])</span><br><span style="color: hsl(0, 100%, 40%);">-            reactor.callLater(delay, testami.sendMessage, message)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def run(self, junk=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        self.__iterate()</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 multiIterator(object):</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self, test_object, scenariosequences, actions):</span><br><span style="color: hsl(0, 100%, 40%);">-        self.test_object = test_object</span><br><span style="color: hsl(0, 100%, 40%);">-        self.scenariosequences = scenariosequences</span><br><span style="color: hsl(0, 100%, 40%);">-        self.actions = actions</span><br><span style="color: hsl(0, 100%, 40%);">-        self.iteration = 0</span><br><span style="color: hsl(0, 100%, 40%);">-        self.sequencecounter = 1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __iterate(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        try:</span><br><span style="color: hsl(0, 100%, 40%);">-            sippsequence = self.scenariosequences[self.iteration]</span><br><span style="color: hsl(0, 100%, 40%);">-        except IndexError:</span><br><span style="color: hsl(0, 100%, 40%);">-            LOGGER.warning("End of scenarios list without proper termination")</span><br><span style="color: hsl(0, 100%, 40%);">-            sippsequence = sipp_terminator</span><br><span style="color: hsl(0, 100%, 40%);">-        try:</span><br><span style="color: hsl(0, 100%, 40%);">-            messagesequence = self.actions[self.iteration]</span><br><span style="color: hsl(0, 100%, 40%);">-        except IndexError:</span><br><span style="color: hsl(0, 100%, 40%);">-            LOGGER.warning("End of action list without proper termination")</span><br><span style="color: hsl(0, 100%, 40%);">-            messagesequence = empty_action_list</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        self.iteration += 1</span><br><span style="color: hsl(0, 100%, 40%);">-        if sippsequence['Name'] == 'none':</span><br><span style="color: hsl(0, 100%, 40%);">-            # skip ahead to the next iteration but send any AMI actions if</span><br><span style="color: hsl(0, 100%, 40%);">-            # set, one second apart. Run the next iteration based on how far</span><br><span style="color: hsl(0, 100%, 40%);">-            # out we've scheduled messages.</span><br><span style="color: hsl(0, 100%, 40%);">-            sequencedelay = self.__sendMessages(messagesequence['Messages'], 1)</span><br><span style="color: hsl(0, 100%, 40%);">-            reactor.callLater(sequencedelay, self.run)</span><br><span style="color: hsl(0, 100%, 40%);">-        elif sippsequence['Name'] != 'done':</span><br><span style="color: hsl(0, 100%, 40%);">-            # A scenaro sequence was specified so run it then schedule the</span><br><span style="color: hsl(0, 100%, 40%);">-            # AMI event(s) normally. Set the delay equal to how many scenarios</span><br><span style="color: hsl(0, 100%, 40%);">-            # were registered (as seconds) to try and scale against complexity</span><br><span style="color: hsl(0, 100%, 40%);">-            LOGGER.info("Starting sipp sequence %s" % sippsequence['Name'])</span><br><span style="color: hsl(0, 100%, 40%);">-            self.__startScenarios(sippsequence['sequence'])</span><br><span style="color: hsl(0, 100%, 40%);">-            self.__sendMessages(messagesequence['Messages'], self.sequencecounter)</span><br><span style="color: hsl(0, 100%, 40%);">-        else:</span><br><span style="color: hsl(0, 100%, 40%);">-            # At the final iteration, send any final AMI</span><br><span style="color: hsl(0, 100%, 40%);">-            sequencedelay = self.__sendMessages(messagesequence['Messages'], 0)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __startScenarios(self, sippscenarios):</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        sipp_sequence = SIPpScenarioSequence(self.test_object,</span><br><span style="color: hsl(0, 100%, 40%);">-                                             fail_on_any=True,</span><br><span style="color: hsl(0, 100%, 40%);">-                                             stop_on_done=False)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.sequencecounter = 0</span><br><span style="color: hsl(0, 100%, 40%);">-        for scenario in sippscenarios:</span><br><span style="color: hsl(0, 100%, 40%);">-            LOGGER.info("Adding scenario %s to sequence" % scenario['Name'])</span><br><span style="color: hsl(0, 100%, 40%);">-            sipp_scenario = SIPpScenario(self.test_object.test_name,</span><br><span style="color: hsl(0, 100%, 40%);">-                                         {'scenario': scenario['Name'],</span><br><span style="color: hsl(0, 100%, 40%);">-                                          '-p': scenario['port']},</span><br><span style="color: hsl(0, 100%, 40%);">-                                         target=scenario['target'])</span><br><span style="color: hsl(0, 100%, 40%);">-            sipp_sequence.register_scenario(sipp_scenario)</span><br><span style="color: hsl(0, 100%, 40%);">-            # keep track of how many scenarios we register so we don't</span><br><span style="color: hsl(0, 100%, 40%);">-            # iterate until we have stop_callback hits for all of them</span><br><span style="color: hsl(0, 100%, 40%);">-            self.sequencecounter += 1</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        sipp_sequence.register_scenario_stop_callback(self.run)</span><br><span style="color: hsl(0, 100%, 40%);">-        sipp_sequence.execute()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __sendMessages(self, messages, delay = 2):</span><br><span style="color: hsl(0, 100%, 40%);">-        testami = self.test_object.ami[0]</span><br><span style="color: hsl(0, 100%, 40%);">-        messagedelay = delay</span><br><span style="color: hsl(0, 100%, 40%);">-        for message in messages:</span><br><span style="color: hsl(0, 100%, 40%);">-            if message['Action'] !='none':</span><br><span style="color: hsl(0, 100%, 40%);">-                LOGGER.info("Scheduling AMI %s" % message['Action'])</span><br><span style="color: hsl(0, 100%, 40%);">-                reactor.callLater(delay, testami.sendMessage, message)</span><br><span style="color: hsl(0, 100%, 40%);">-                # spread out the messages by 2 seconds</span><br><span style="color: hsl(0, 100%, 40%);">-                messagedelay += 2</span><br><span style="color: hsl(0, 100%, 40%);">-        # return last action's delay + 2, ie when to run the next</span><br><span style="color: hsl(0, 100%, 40%);">-        # command if you want it evenly distributed and after the</span><br><span style="color: hsl(0, 100%, 40%);">-        # last message scheduled here</span><br><span style="color: hsl(0, 100%, 40%);">-        return messagedelay</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def run(self, junk=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        # only run the next iteration once there is a call-back for</span><br><span style="color: hsl(0, 100%, 40%);">-        # each sipp scenario in the sequence.  Otherwise, decrement</span><br><span style="color: hsl(0, 100%, 40%);">-        # until we get there</span><br><span style="color: hsl(0, 100%, 40%);">-        if self.sequencecounter == 1:</span><br><span style="color: hsl(0, 100%, 40%);">-            self.__iterate()</span><br><span style="color: hsl(0, 100%, 40%);">-        else:</span><br><span style="color: hsl(0, 100%, 40%);">-            self.sequencecounter -= 1</span><br><span>diff --git a/lib/python/asterisk/sipp_iterator.py b/lib/python/asterisk/sipp_iterator.py</span><br><span>new file mode 100644</span><br><span>index 0000000..2598852</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/sipp_iterator.py</span><br><span>@@ -0,0 +1,368 @@</span><br><span style="color: hsl(120, 100%, 40%);">+'''</span><br><span style="color: hsl(120, 100%, 40%);">+Copyright (C) 2022, Sangoma Technologies Corp</span><br><span style="color: hsl(120, 100%, 40%);">+Mike Bradeen <mbradeen@sangoma.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%);">+The sipp_iterator is designed to let us start sipp, then perform an ami</span><br><span style="color: hsl(120, 100%, 40%);">+action as a cycle of start sipp->generate event(s)->sipp stops, start sipp...</span><br><span style="color: hsl(120, 100%, 40%);">+This is for tests that are performing the same test different ways or performing</span><br><span style="color: hsl(120, 100%, 40%);">+different iterations of the same test.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+An example would be to start a scenario that waits for a NOTIFY, then send the AMI</span><br><span style="color: hsl(120, 100%, 40%);">+to generate that NOTIFY, then starts a second scenario, send a second AMI, etc.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+There are two types for this, 'single' and 'multi'.  'single' is one-to-one, with each</span><br><span style="color: hsl(120, 100%, 40%);">+scenario start triggering the next ami and the scenario stop triggering the next scenario</span><br><span style="color: hsl(120, 100%, 40%);">+start.  It is fed a pair of lists, one is a list of scenarios and the other is a list of</span><br><span style="color: hsl(120, 100%, 40%);">+AMI actions.  After the final scenario, we add a 'done' indicator to the sipp side and a</span><br><span style="color: hsl(120, 100%, 40%);">+final event on the AMI side, examples:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+sipp-config:</span><br><span style="color: hsl(120, 100%, 40%);">+    type: 'single'</span><br><span style="color: hsl(120, 100%, 40%);">+    scenarios:</span><br><span style="color: hsl(120, 100%, 40%);">+        # Alice registers, no AMI action</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'alice-registers.xml', 'port': '5061', 'target': '127.0.0.1', 'ordered-args': {'-aa'}},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Action': 'none'}}</span><br><span style="color: hsl(120, 100%, 40%);">+        # Alice waits for a NOTIFY, generated by MWIUpdate</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'alice-is-notified-1.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Action': 'MWIUpdate', 'Mailbox': 'alice', 'NewMessages':'2', 'OldMessages':'0'}}</span><br><span style="color: hsl(120, 100%, 40%);">+        # Alice waits for a NOTIFY, generated by MWIUpdate</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'alice-is-notified-2.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Action': 'MWIUpdate', 'Mailbox': 'alice', 'NewMessages':'1', 'OldMessages':'1'}}</span><br><span style="color: hsl(120, 100%, 40%);">+        # Alice waits for a NOTIFY, generated by MWIUpdate</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'alice-is-notified-3.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Action': 'MWIUpdate', 'Mailbox': 'alice', 'NewMessages':'0', 'OldMessages':'2'}}</span><br><span style="color: hsl(120, 100%, 40%);">+        # Alice waits for a NOTIFY, generated by MWIDelete</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'alice-is-notified-4.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Action': 'MWIDelete', 'Mailbox': 'alice'}}</span><br><span style="color: hsl(120, 100%, 40%);">+        # indicate no more scenarios to run, send testComplete Event</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'done'},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Action': 'UserEvent', 'UserEvent': 'testComplete'}}</span><br><span style="color: hsl(120, 100%, 40%);">+-or-</span><br><span style="color: hsl(120, 100%, 40%);">+    type: 'multi'</span><br><span style="color: hsl(120, 100%, 40%);">+    scenarios:</span><br><span style="color: hsl(120, 100%, 40%);">+        # Alice and bob register, receive initial NOTIFYs. No actions</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'register', 'sequence': [</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Name': 'alice-registers.xml', 'port': '5061', 'target': '127.0.0.1', 'ordered-args': {'-aa'}},</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Name': 'bob-registers.xml', 'port': '5062', 'target': '127.0.0.1', 'ordered-args': {'-aa'}} ]},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Messages': [</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Action': 'none'} ]}}</span><br><span style="color: hsl(120, 100%, 40%);">+        # Alice and Bob are both sent NOTIFY messages, triggered by the MWIUpdate</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'mailbox_a', 'sequence': [</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Name': 'alice-is-notified-1.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Name': 'bob-is-notified-1.xml', 'port': '5062', 'target': '127.0.0.1'} ]},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Messages': [</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Action': 'MWIUpdate', 'Mailbox': 'mailbox_a', 'NewMessages':'2', 'OldMessages':'1'} ]}}</span><br><span style="color: hsl(120, 100%, 40%);">+        # Alice and Bob are both sent NOTIFY messages, triggered by the MWIUpdate</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'mailbox_b', 'sequence': [</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Name': 'alice-is-notified-2.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Name': 'bob-is-notified-2.xml', 'port': '5062', 'target': '127.0.0.1'} ]},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Messages': [</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Action': 'MWIUpdate', 'Mailbox': 'mailbox_b', 'NewMessages':'3', 'OldMessages':'3'} ]}}</span><br><span style="color: hsl(120, 100%, 40%);">+        # indicate no more scenarios to run, send testComplete Event</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'done'},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Messages': [</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Action': 'UserEvent', 'UserEvent': 'testComplete'} ]}}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+In the above examples, we end the test with a testComplete event that we then use to trigger the</span><br><span style="color: hsl(120, 100%, 40%);">+test stop event in the yaml file, example:</span><br><span style="color: hsl(120, 100%, 40%);">+ami-config:</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: 'UserEvent'</span><br><span style="color: hsl(120, 100%, 40%);">+                    UserEvent: 'testComplete'</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 style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+A Name of 'none' for a scenario(list) or an Action of 'none' skips that particular scenario or action,</span><br><span style="color: hsl(120, 100%, 40%);">+but not the other. This is mostly to allow one or more AMI events to be triggered before the</span><br><span style="color: hsl(120, 100%, 40%);">+corresponding sipp scenario is started but could also allow for intermediate sipp scenarios. Setting</span><br><span style="color: hsl(120, 100%, 40%);">+both to 'none' would be functionally the same as waiting for 1 second before going on to the next</span><br><span style="color: hsl(120, 100%, 40%);">+iteration.</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 sys</span><br><span style="color: hsl(120, 100%, 40%);">+import logging</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from . import test_suite_utils</span><br><span style="color: hsl(120, 100%, 40%);">+from abc import ABCMeta, abstractmethod</span><br><span style="color: hsl(120, 100%, 40%);">+from twisted.internet import reactor, defer, protocol, error</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 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 style="color: hsl(120, 100%, 40%);">+    PLUGGABLE_ACTION_REGISTRY</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.sipp import SIPpScenarioSequence</span><br><span style="color: hsl(120, 100%, 40%);">+from asterisk.sipp import SIPpScenario</span><br><span style="color: hsl(120, 100%, 40%);">+from twisted.internet import reactor</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+LOGGER = logging.getLogger(__name__)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+sipp_terminator = {'Name': 'done'}</span><br><span style="color: hsl(120, 100%, 40%);">+empty_scenario = {'Name': 'none'}</span><br><span style="color: hsl(120, 100%, 40%);">+empty_action = {'Action': 'none'}</span><br><span style="color: hsl(120, 100%, 40%);">+empty_action_list = {'Messages': [{'Action': 'none'}] }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class SIPpIteratorTestCase(TestCase):</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, test_path, test_config):</span><br><span style="color: hsl(120, 100%, 40%);">+        super(SIPpIteratorTestCase, self).__init__(test_path, test_config=test_config)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if not test_config:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise ValueError("SIPpIteratorTestCase requires a test config")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        self._test_config = test_config</span><br><span style="color: hsl(120, 100%, 40%);">+        self._current_test = 0</span><br><span style="color: hsl(120, 100%, 40%);">+        self._connected_amis = 0</span><br><span style="color: hsl(120, 100%, 40%);">+        self._actor = None</span><br><span style="color: hsl(120, 100%, 40%);">+        self.asterisk_instances = test_config.get('asterisk-instances') or 1</span><br><span style="color: hsl(120, 100%, 40%);">+        self.connect_ami = test_config.get('connect-ami') or False</span><br><span style="color: hsl(120, 100%, 40%);">+        self.create_asterisk(count=self.asterisk_instances, test_config=test_config)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def on_reactor_timeout(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Create a failure token when the test times out"""</span><br><span style="color: hsl(120, 100%, 40%);">+        self.create_fail_token("Reactor timed out. Test Failed.")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def run(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Override of the run method.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        Create an AMI factory in case anyone wants it</span><br><span style="color: hsl(120, 100%, 40%);">+        """</span><br><span style="color: hsl(120, 100%, 40%);">+        super(SIPpIteratorTestCase, self).run()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        for a in range(1, self.asterisk_instances):</span><br><span style="color: hsl(120, 100%, 40%);">+                self.ast[a].cli_exec('sip set debug on')</span><br><span style="color: hsl(120, 100%, 40%);">+                self.ast[a].cli_exec('pjsip set logger on')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGGER.info("creating ami factory")</span><br><span style="color: hsl(120, 100%, 40%);">+        if not isinstance(self.connect_ami, dict):</span><br><span style="color: hsl(120, 100%, 40%);">+            self.create_ami_factory(count=self.asterisk_instances)</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.create_ami_factory(**self.connect_ami)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def stop_asterisk(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Kill any remaining SIPp scenarios"""</span><br><span style="color: hsl(120, 100%, 40%);">+        if self._actor:</span><br><span style="color: hsl(120, 100%, 40%);">+            self._actor.terminate()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def ami_connect(self, ami):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Handler for the AMI connect event"""</span><br><span style="color: hsl(120, 100%, 40%);">+        super(SIPpIteratorTestCase, self).ami_connect(ami)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        # keep track of number of connected amis and only start once they</span><br><span style="color: hsl(120, 100%, 40%);">+        # are all connected</span><br><span style="color: hsl(120, 100%, 40%);">+        self._connected_amis +=1</span><br><span style="color: hsl(120, 100%, 40%);">+        if self._connected_amis == self.asterisk_instances:</span><br><span style="color: hsl(120, 100%, 40%);">+            self._execute_test()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def ami_reconnect(self, ami):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Handler for the AMI reconnect event"""</span><br><span style="color: hsl(120, 100%, 40%);">+        super(SIPpIteratorTestCase, self).ami_reconnect(ami)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        for a in range(1, self.asterisk_instances):</span><br><span style="color: hsl(120, 100%, 40%);">+                self.ast[a].cli_exec('sip set debug on')</span><br><span style="color: hsl(120, 100%, 40%);">+                self.ast[a].cli_exec('pjsip set logger on')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _execute_test(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        self._actor = SIPpIteratorActionModule(self, self._test_config)</span><br><span style="color: hsl(120, 100%, 40%);">+        self._actor.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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class singleIterator(object):</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, test_object, scenarios):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.test_object = test_object</span><br><span style="color: hsl(120, 100%, 40%);">+        self.scenarios = scenarios</span><br><span style="color: hsl(120, 100%, 40%);">+        self.activescenario = None</span><br><span style="color: hsl(120, 100%, 40%);">+        self.iteration = 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __iterate(self):</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%);">+            scenariocontainer = self.scenarios[self.iteration]</span><br><span style="color: hsl(120, 100%, 40%);">+            scenario = scenariocontainer['scenario'] or empty_scenario</span><br><span style="color: hsl(120, 100%, 40%);">+            message = scenariocontainer['action'] or empty_action</span><br><span style="color: hsl(120, 100%, 40%);">+        except IndexError:</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGGER.warning("End of scenario list without proper termination")</span><br><span style="color: hsl(120, 100%, 40%);">+            scenario = sipp_terminator</span><br><span style="color: hsl(120, 100%, 40%);">+            message = empty_action</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        self.iteration += 1</span><br><span style="color: hsl(120, 100%, 40%);">+        if scenario['Name'] == 'none':</span><br><span style="color: hsl(120, 100%, 40%);">+            # skip ahead to the next iteration but send the AMI</span><br><span style="color: hsl(120, 100%, 40%);">+            # action if set. Speed up the iteration.</span><br><span style="color: hsl(120, 100%, 40%);">+            self.__sendMessage(message, 0)</span><br><span style="color: hsl(120, 100%, 40%);">+            reactor.callLater(1, self.run)</span><br><span style="color: hsl(120, 100%, 40%);">+        elif scenario['Name'] != 'done':</span><br><span style="color: hsl(120, 100%, 40%);">+            # A scenaro was specified so run it then schedule the</span><br><span style="color: hsl(120, 100%, 40%);">+            # AMI event if there is one.</span><br><span style="color: hsl(120, 100%, 40%);">+            self.__startScenario(scenario)</span><br><span style="color: hsl(120, 100%, 40%);">+            self.__sendMessage(message)</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            # At the final iteration, send any final AMI immediately</span><br><span style="color: hsl(120, 100%, 40%);">+            self.__sendMessage(message, 0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __startScenario(self, scenario):</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGGER.info("Starting sipp scenario %s" % scenario['Name'])</span><br><span style="color: hsl(120, 100%, 40%);">+        self.activescenario = SIPpScenario(self.test_object.test_name,</span><br><span style="color: hsl(120, 100%, 40%);">+                                     {'scenario': scenario['Name'],</span><br><span style="color: hsl(120, 100%, 40%);">+                                      '-p': scenario['port']},</span><br><span style="color: hsl(120, 100%, 40%);">+                                      scenario.get('ordered-args') or [],</span><br><span style="color: hsl(120, 100%, 40%);">+                                     target=scenario['target'])</span><br><span style="color: hsl(120, 100%, 40%);">+        exiter = self.activescenario.run(self.test_object)</span><br><span style="color: hsl(120, 100%, 40%);">+        exiter.addCallback(self.run)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __sendMessage(self, message, delay=2):</span><br><span style="color: hsl(120, 100%, 40%);">+        if message['Action'] != 'none':</span><br><span style="color: hsl(120, 100%, 40%);">+            testami = self.test_object.ami[0]</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGGER.info("Scheduling AMI %s" % message['Action'])</span><br><span style="color: hsl(120, 100%, 40%);">+            reactor.callLater(delay, testami.sendMessage, message)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def run(self, junk=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.__iterate()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def terminate(self, junk=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Kill active scenario if still in existence"""</span><br><span style="color: hsl(120, 100%, 40%);">+        if not self.activescenario.exited:</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGGER.warn("SIPp Scenario %s has not exited; killing" %</span><br><span style="color: hsl(120, 100%, 40%);">+                        self.activescenario.name)</span><br><span style="color: hsl(120, 100%, 40%);">+            self.activescenario.kill()</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 multiIterator(object):</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, test_object, scenariosequences):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.test_object = test_object</span><br><span style="color: hsl(120, 100%, 40%);">+        self.scenariosequences = scenariosequences</span><br><span style="color: hsl(120, 100%, 40%);">+        self.activescenarios = []</span><br><span style="color: hsl(120, 100%, 40%);">+        self.iteration = 0</span><br><span style="color: hsl(120, 100%, 40%);">+        self.sequencecounter = 1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __iterate(self):</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%);">+            scenariosequence = self.scenariosequences[self.iteration]</span><br><span style="color: hsl(120, 100%, 40%);">+            sippsequence = scenariosequence['scenario'] or empty_scenario</span><br><span style="color: hsl(120, 100%, 40%);">+            messagesequence = scenariosequence['action'] or empty_action_list</span><br><span style="color: hsl(120, 100%, 40%);">+        except IndexError:</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGGER.warning("End of scenario list without proper termination")</span><br><span style="color: hsl(120, 100%, 40%);">+            sippsequence = sipp_terminator</span><br><span style="color: hsl(120, 100%, 40%);">+            messagesequence = empty_action_list</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        self.iteration += 1</span><br><span style="color: hsl(120, 100%, 40%);">+        if sippsequence['Name'] == 'none':</span><br><span style="color: hsl(120, 100%, 40%);">+            # skip ahead to the next iteration but send any AMI actions if</span><br><span style="color: hsl(120, 100%, 40%);">+            # set, one second apart. Run the next iteration based on how far</span><br><span style="color: hsl(120, 100%, 40%);">+            # out we've scheduled messages.</span><br><span style="color: hsl(120, 100%, 40%);">+            sequencedelay = self.__sendMessages(messagesequence['Messages'], 1)</span><br><span style="color: hsl(120, 100%, 40%);">+            reactor.callLater(sequencedelay, self.run)</span><br><span style="color: hsl(120, 100%, 40%);">+        elif sippsequence['Name'] != 'done':</span><br><span style="color: hsl(120, 100%, 40%);">+            # A scenaro sequence was specified so run it then schedule the</span><br><span style="color: hsl(120, 100%, 40%);">+            # AMI event(s) normally. Set the delay equal to how many scenarios</span><br><span style="color: hsl(120, 100%, 40%);">+            # were registered (as seconds) to try and scale against complexity</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGGER.info("Starting sipp sequence %s" % sippsequence['Name'])</span><br><span style="color: hsl(120, 100%, 40%);">+            self.__startScenarios(sippsequence['sequence'])</span><br><span style="color: hsl(120, 100%, 40%);">+            self.__sendMessages(messagesequence['Messages'], self.sequencecounter)</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            # At the final iteration, send any final AMI</span><br><span style="color: hsl(120, 100%, 40%);">+            sequencedelay = self.__sendMessages(messagesequence['Messages'], 0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __startScenarios(self, sippscenarios):</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        sipp_sequence = SIPpScenarioSequence(self.test_object,</span><br><span style="color: hsl(120, 100%, 40%);">+                                             fail_on_any=True,</span><br><span style="color: hsl(120, 100%, 40%);">+                                             stop_on_done=False)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.sequencecounter = 0</span><br><span style="color: hsl(120, 100%, 40%);">+        self.activescenarios.clear()</span><br><span style="color: hsl(120, 100%, 40%);">+        for scenario in sippscenarios:</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGGER.info("Adding scenario %s to sequence" % scenario['Name'])</span><br><span style="color: hsl(120, 100%, 40%);">+            sipp_scenario = SIPpScenario(self.test_object.test_name,</span><br><span style="color: hsl(120, 100%, 40%);">+                                         {'scenario': scenario['Name'],</span><br><span style="color: hsl(120, 100%, 40%);">+                                          '-p': scenario['port']},</span><br><span style="color: hsl(120, 100%, 40%);">+                                          scenario.get('ordered-args') or [],</span><br><span style="color: hsl(120, 100%, 40%);">+                                         target=scenario['target'])</span><br><span style="color: hsl(120, 100%, 40%);">+            sipp_sequence.register_scenario(sipp_scenario)</span><br><span style="color: hsl(120, 100%, 40%);">+            # keep track of how many scenarios we register so we don't</span><br><span style="color: hsl(120, 100%, 40%);">+            # iterate until we have stop_callback hits for all of them</span><br><span style="color: hsl(120, 100%, 40%);">+            self.sequencecounter += 1</span><br><span style="color: hsl(120, 100%, 40%);">+            self.activescenarios.append(sipp_scenario)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        sipp_sequence.register_scenario_stop_callback(self.run)</span><br><span style="color: hsl(120, 100%, 40%);">+        sipp_sequence.execute()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __sendMessages(self, messages, delay = 2):</span><br><span style="color: hsl(120, 100%, 40%);">+        testami = self.test_object.ami[0]</span><br><span style="color: hsl(120, 100%, 40%);">+        messagedelay = delay</span><br><span style="color: hsl(120, 100%, 40%);">+        for message in messages:</span><br><span style="color: hsl(120, 100%, 40%);">+            if message['Action'] !='none':</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGGER.info("Scheduling AMI %s" % message['Action'])</span><br><span style="color: hsl(120, 100%, 40%);">+                reactor.callLater(delay, testami.sendMessage, message)</span><br><span style="color: hsl(120, 100%, 40%);">+                # spread out the messages by 2 seconds</span><br><span style="color: hsl(120, 100%, 40%);">+                messagedelay += 2</span><br><span style="color: hsl(120, 100%, 40%);">+        # return last action's delay + 2, ie when to run the next</span><br><span style="color: hsl(120, 100%, 40%);">+        # command if you want it evenly distributed and after the</span><br><span style="color: hsl(120, 100%, 40%);">+        # last message scheduled here</span><br><span style="color: hsl(120, 100%, 40%);">+        return messagedelay</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def run(self, junk=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        # only run the next iteration once there is a call-back for</span><br><span style="color: hsl(120, 100%, 40%);">+        # each sipp scenario in the sequence.  Otherwise, decrement</span><br><span style="color: hsl(120, 100%, 40%);">+        # until we get there</span><br><span style="color: hsl(120, 100%, 40%);">+        if self.sequencecounter == 1:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.__iterate()</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.sequencecounter -= 1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def terminate(self, junk=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Kill any scenarios still in existence"""</span><br><span style="color: hsl(120, 100%, 40%);">+        for scenario in self.activescenarios:</span><br><span style="color: hsl(120, 100%, 40%);">+            if not scenario.exited:</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGGER.warn("SIPp Scenario %s has not exited; killing" %</span><br><span style="color: hsl(120, 100%, 40%);">+                            scenario.name)</span><br><span style="color: hsl(120, 100%, 40%);">+                scenario.kill()</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 style="color: hsl(120, 100%, 40%);">+class SIPpIteratorActionModule(object):</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, test_object, config):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Initialize iterator 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%);">+            LOGGER.error("No registered SIPp scenarios, required for test type.")</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%);">+        type = config.get('type', 'multi')</span><br><span style="color: hsl(120, 100%, 40%);">+        if (type == 'multi'):</span><br><span style="color: hsl(120, 100%, 40%);">+            self.testrunner = multiIterator(test_object, scenarios)</span><br><span style="color: hsl(120, 100%, 40%);">+        elif (type == 'single'):</span><br><span style="color: hsl(120, 100%, 40%);">+            self.testrunner = singleIterator(test_object, scenarios)</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGGER.error("Unknown iteration type, must be single or multi")</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%);">+    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%);">+        self.testrunner.run(triggered_by, source, extra)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def execute(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Execute specified SIPp scenarios"""</span><br><span style="color: hsl(120, 100%, 40%);">+        self.testrunner.run()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def terminate(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.testrunner.terminate()</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("sippiterator", SIPpIteratorActionModule)</span><br><span>\ No newline at end of file</span><br><span>diff --git a/tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/mwi_check.py b/tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/mwi_check.py</span><br><span>deleted file mode 100644</span><br><span>index 9fadeb0..0000000</span><br><span>--- a/tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/mwi_check.py</span><br><span>+++ /dev/null</span><br><span>@@ -1,46 +0,0 @@</span><br><span style="color: hsl(0, 100%, 40%);">-#!/usr/bin/env python</span><br><span style="color: hsl(0, 100%, 40%);">-"""Pluggable modules for the mwi_aggregate test</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%);">-import mailbox</span><br><span style="color: hsl(0, 100%, 40%);">-import sys</span><br><span style="color: hsl(0, 100%, 40%);">-import logging</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-sys.path.append("lib/python")</span><br><span style="color: hsl(0, 100%, 40%);">-from asterisk.scenario_iterator import multiIterator</span><br><span style="color: hsl(0, 100%, 40%);">-</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%);">-mwiscenarios = [</span><br><span style="color: hsl(0, 100%, 40%);">-        {'Name': 'mailbox_a', 'sequence': [</span><br><span style="color: hsl(0, 100%, 40%);">-                {'Name': 'alice-is-notified-1.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(0, 100%, 40%);">-                {'Name': 'bob-is-notified-1.xml', 'port': '5062', 'target': '127.0.0.1'} ]},</span><br><span style="color: hsl(0, 100%, 40%);">-        {'Name': 'mailbox_b', 'sequence': [</span><br><span style="color: hsl(0, 100%, 40%);">-                {'Name': 'alice-is-notified-2.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(0, 100%, 40%);">-                {'Name': 'bob-is-notified-2.xml', 'port': '5062', 'target': '127.0.0.1'} ]},</span><br><span style="color: hsl(0, 100%, 40%);">-        {'Name': 'done'}</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%);">-mwis = [</span><br><span style="color: hsl(0, 100%, 40%);">-        {'Messages': [</span><br><span style="color: hsl(0, 100%, 40%);">-                {'Action': 'MWIUpdate', 'Mailbox': 'mailbox_a', 'NewMessages':'2', 'OldMessages':'1'} ]},</span><br><span style="color: hsl(0, 100%, 40%);">-        {'Messages': [</span><br><span style="color: hsl(0, 100%, 40%);">-                {'Action': 'MWIUpdate', 'Mailbox': 'mailbox_b', 'NewMessages':'3', 'OldMessages':'3'} ]},</span><br><span style="color: hsl(0, 100%, 40%);">-        {'Messages': [</span><br><span style="color: hsl(0, 100%, 40%);">-                {'Action': 'UserEvent', 'UserEvent': 'testComplete'} ]}</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 start_test(test_object, junk):</span><br><span style="color: hsl(0, 100%, 40%);">-    LOGGER.info("Starting mwi_check")</span><br><span style="color: hsl(0, 100%, 40%);">-    testrunner = multiIterator(test_object, mwiscenarios, mwis)</span><br><span style="color: hsl(0, 100%, 40%);">-    testrunner.run(junk)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-def stop_test():</span><br><span style="color: hsl(0, 100%, 40%);">-    return</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>diff --git a/tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/test-config.yaml b/tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/test-config.yaml</span><br><span>index cabe5e2..d7f29e6 100644</span><br><span>--- a/tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/test-config.yaml</span><br><span>+++ b/tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/test-config.yaml</span><br><span>@@ -28,7 +28,7 @@</span><br><span>     add-test-to-search-path: 'True'</span><br><span>     test-object:</span><br><span>         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%);">+        typename: 'sipp_iterator.SIPpIteratorTestCase'</span><br><span>     modules:</span><br><span>         -</span><br><span>             config-section: 'ami-config'</span><br><span>@@ -37,19 +37,31 @@</span><br><span> sipp-config:</span><br><span>     connect-ami: 'True'</span><br><span>     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%);">-    stop-after-scenarios: False</span><br><span style="color: hsl(0, 100%, 40%);">-    stop_callback_module: 'mwi_check'</span><br><span style="color: hsl(0, 100%, 40%);">-    stop_callback_method: 'start_test'</span><br><span style="color: hsl(0, 100%, 40%);">-    test-iterations:</span><br><span style="color: hsl(0, 100%, 40%);">-        # We pass the initial registers the -aa flag then let them run for a second so they can get the</span><br><span style="color: hsl(0, 100%, 40%);">-        # 1-2 initial NOTIFY messages we don't care about.  The MWI AMI updates are set to start after</span><br><span style="color: hsl(0, 100%, 40%);">-        -</span><br><span style="color: hsl(0, 100%, 40%);">-            scenarios:</span><br><span style="color: hsl(0, 100%, 40%);">-                - { 'key-args': {'scenario': 'alice-registers.xml', '-p': '5061'},</span><br><span style="color: hsl(0, 100%, 40%);">-                    'ordered-args': {'-aa'} }</span><br><span style="color: hsl(0, 100%, 40%);">-                - { 'key-args': {'scenario': 'bob-registers.xml', '-p': '5062'},</span><br><span style="color: hsl(0, 100%, 40%);">-                    'ordered-args': {'-aa'}  }</span><br><span style="color: hsl(120, 100%, 40%);">+    type: 'multi'</span><br><span style="color: hsl(120, 100%, 40%);">+    scenarios:</span><br><span style="color: hsl(120, 100%, 40%);">+        # Alice and bob register, receive initial NOTIFYs. No actions</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'register', 'sequence': [</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Name': 'alice-registers.xml', 'port': '5061', 'target': '127.0.0.1', 'ordered-args': {'-aa'}},</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Name': 'bob-registers.xml', 'port': '5062', 'target': '127.0.0.1', 'ordered-args': {'-aa'}} ]},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Messages': [</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Action': 'none'} ]}}</span><br><span style="color: hsl(120, 100%, 40%);">+        # Alice and Bob are both sent NOTIFY messages, triggered by the MWIUpdate</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'mailbox_a', 'sequence': [</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Name': 'alice-is-notified-1.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Name': 'bob-is-notified-1.xml', 'port': '5062', 'target': '127.0.0.1'} ]},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Messages': [</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Action': 'MWIUpdate', 'Mailbox': 'mailbox_a', 'NewMessages':'2', 'OldMessages':'1'} ]}}</span><br><span style="color: hsl(120, 100%, 40%);">+        # Alice and Bob are both sent NOTIFY messages, triggered by the MWIUpdate</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'mailbox_b', 'sequence': [</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Name': 'alice-is-notified-2.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Name': 'bob-is-notified-2.xml', 'port': '5062', 'target': '127.0.0.1'} ]},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Messages': [</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Action': 'MWIUpdate', 'Mailbox': 'mailbox_b', 'NewMessages':'3', 'OldMessages':'3'} ]}}</span><br><span style="color: hsl(120, 100%, 40%);">+        # indicate no more scenarios to run, send testComplete Event</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'done'},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Messages': [</span><br><span style="color: hsl(120, 100%, 40%);">+                {'Action': 'UserEvent', 'UserEvent': 'testComplete'} ]}}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ami-config:</span><br><span>     -</span><br><span>         ami-events:</span><br><span>@@ -59,3 +71,4 @@</span><br><span>                     UserEvent: 'testComplete'</span><br><span>             count: 1</span><br><span>         stop_test:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>diff --git a/tests/channels/pjsip/subscriptions/mwi/unsolicited/mailbox_count_changes/mwi_check.py b/tests/channels/pjsip/subscriptions/mwi/unsolicited/mailbox_count_changes/mwi_check.py</span><br><span>deleted file mode 100644</span><br><span>index 28c4ded..0000000</span><br><span>--- a/tests/channels/pjsip/subscriptions/mwi/unsolicited/mailbox_count_changes/mwi_check.py</span><br><span>+++ /dev/null</span><br><span>@@ -1,36 +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%);">-import sys</span><br><span style="color: hsl(0, 100%, 40%);">-import logging</span><br><span style="color: hsl(0, 100%, 40%);">-</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%);">-from twisted.internet import reactor</span><br><span style="color: hsl(0, 100%, 40%);">-from asterisk.scenario_iterator import singleIterator</span><br><span style="color: hsl(0, 100%, 40%);">-</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%);">-mwiscenarios = [</span><br><span style="color: hsl(0, 100%, 40%);">-    {'Name': 'alice-is-notified-1.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(0, 100%, 40%);">-    {'Name': 'alice-is-notified-2.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(0, 100%, 40%);">-    {'Name': 'alice-is-notified-3.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(0, 100%, 40%);">-    {'Name': 'alice-is-notified-4.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(0, 100%, 40%);">-    {'Name': 'done'}</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%);">-mwis = [</span><br><span style="color: hsl(0, 100%, 40%);">-        {'Action': 'MWIUpdate', 'Mailbox': 'alice', 'NewMessages':'2', 'OldMessages':'0'},</span><br><span style="color: hsl(0, 100%, 40%);">-        {'Action': 'MWIUpdate', 'Mailbox': 'alice', 'NewMessages':'1', 'OldMessages':'1'},</span><br><span style="color: hsl(0, 100%, 40%);">-        {'Action': 'MWIUpdate', 'Mailbox': 'alice', 'NewMessages':'0', 'OldMessages':'2'},</span><br><span style="color: hsl(0, 100%, 40%);">-        {'Action': 'MWIDelete', 'Mailbox': 'alice'},</span><br><span style="color: hsl(0, 100%, 40%);">-        {'Action': 'UserEvent', 'UserEvent': 'testComplete'}</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 start_test(test_object, junk):</span><br><span style="color: hsl(0, 100%, 40%);">-    LOGGER.info("Starting mwi_check")</span><br><span style="color: hsl(0, 100%, 40%);">-    testrunner = singleIterator(test_object, mwiscenarios, mwis)</span><br><span style="color: hsl(0, 100%, 40%);">-    testrunner.run(junk)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-def stop_test():</span><br><span style="color: hsl(0, 100%, 40%);">-    return</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>diff --git a/tests/channels/pjsip/subscriptions/mwi/unsolicited/mailbox_count_changes/test-config.yaml b/tests/channels/pjsip/subscriptions/mwi/unsolicited/mailbox_count_changes/test-config.yaml</span><br><span>index 14f9456..d895656 100644</span><br><span>--- a/tests/channels/pjsip/subscriptions/mwi/unsolicited/mailbox_count_changes/test-config.yaml</span><br><span>+++ b/tests/channels/pjsip/subscriptions/mwi/unsolicited/mailbox_count_changes/test-config.yaml</span><br><span>@@ -24,7 +24,7 @@</span><br><span>     add-test-to-search-path: 'True'</span><br><span>     test-object:</span><br><span>         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%);">+        typename: 'sipp_iterator.SIPpIteratorTestCase'</span><br><span>     modules:</span><br><span>         -</span><br><span>             config-section: 'ami-config'</span><br><span>@@ -33,17 +33,27 @@</span><br><span> sipp-config:</span><br><span>     connect-ami: 'True'</span><br><span>     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%);">-    stop-after-scenarios: False</span><br><span style="color: hsl(0, 100%, 40%);">-    stop_callback_module: 'mwi_check'</span><br><span style="color: hsl(0, 100%, 40%);">-    stop_callback_method: 'start_test'</span><br><span style="color: hsl(0, 100%, 40%);">-    test-iterations:</span><br><span style="color: hsl(0, 100%, 40%);">-        # We pass the initial registers the -aa flag then let them run for a second so they can get the</span><br><span style="color: hsl(0, 100%, 40%);">-        # 1-2 initial NOTIFY messages we don't care about.  The MWI AMI updates are set to start after</span><br><span style="color: hsl(0, 100%, 40%);">-        -</span><br><span style="color: hsl(0, 100%, 40%);">-            scenarios:</span><br><span style="color: hsl(0, 100%, 40%);">-                - { 'key-args': {'scenario': 'alice-registers.xml', '-p': '5061'},</span><br><span style="color: hsl(0, 100%, 40%);">-                    'ordered-args': {'-aa'} }</span><br><span style="color: hsl(120, 100%, 40%);">+    type: 'single'</span><br><span style="color: hsl(120, 100%, 40%);">+    scenarios:</span><br><span style="color: hsl(120, 100%, 40%);">+        # Alice registers, no AMI action</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'alice-registers.xml', 'port': '5061', 'target': '127.0.0.1', 'ordered-args': {'-aa'}},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Action': 'none'}}</span><br><span style="color: hsl(120, 100%, 40%);">+        # Alice waits for a NOTIFY, generated by MWIUpdate</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'alice-is-notified-1.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Action': 'MWIUpdate', 'Mailbox': 'alice', 'NewMessages':'2', 'OldMessages':'0'}}</span><br><span style="color: hsl(120, 100%, 40%);">+        # Alice waits for a NOTIFY, generated by MWIUpdate</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'alice-is-notified-2.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Action': 'MWIUpdate', 'Mailbox': 'alice', 'NewMessages':'1', 'OldMessages':'1'}}</span><br><span style="color: hsl(120, 100%, 40%);">+        # Alice waits for a NOTIFY, generated by MWIUpdate</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'alice-is-notified-3.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Action': 'MWIUpdate', 'Mailbox': 'alice', 'NewMessages':'0', 'OldMessages':'2'}}</span><br><span style="color: hsl(120, 100%, 40%);">+        # Alice waits for a NOTIFY, generated by MWIDelete</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'alice-is-notified-4.xml', 'port': '5061', 'target': '127.0.0.1'},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Action': 'MWIDelete', 'Mailbox': 'alice'}}</span><br><span style="color: hsl(120, 100%, 40%);">+        # indicate no more scenarios to run, send testComplete Event</span><br><span style="color: hsl(120, 100%, 40%);">+        - { 'scenario': {'Name': 'done'},</span><br><span style="color: hsl(120, 100%, 40%);">+            'action': {'Action': 'UserEvent', 'UserEvent': 'testComplete'}}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ami-config:</span><br><span>     -</span><br><span>         ami-events:</span><br><span>@@ -53,3 +63,4 @@</span><br><span>                     UserEvent: 'testComplete'</span><br><span>             count: 1</span><br><span>         stop_test:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/testsuite/+/19520">change 19520</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/+/19520"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: testsuite </div>
<div style="display:none"> Gerrit-Branch: certified/18.9 </div>
<div style="display:none"> Gerrit-Change-Id: I57ed85d2a2a044207da23a750e433dcc38eb93ff </div>
<div style="display:none"> Gerrit-Change-Number: 19520 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Michael Bradeen <mbradeen@sangoma.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@sangoma.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>