[Asterisk-code-review] sipp_iterator: converting iterator into pluggable module (testsuite[16])

Michael Bradeen asteriskteam at digium.com
Tue Nov 1 10:22:33 CDT 2022


Michael Bradeen has uploaded this change for review. ( https://gerrit.asterisk.org/c/testsuite/+/19517 )


Change subject: sipp_iterator: converting iterator into pluggable module
......................................................................

sipp_iterator: converting iterator into pluggable module

Per ASTERISK~30214, repeating scenario tests required a mechanism
to force scenario/ami action timing.  This change renames the
scenario_iterator sipp_iterator and makes it a pluggable module
that can read normal test yaml files.

The two existing tests that used the iterator have been moved to
the new method, but further work is required to migrate other
tests with this issue

ASTERISK_30271 #noclose

Change-Id: I57ed85d2a2a044207da23a750e433dcc38eb93ff
---
M lib/python/asterisk/pluggable_modules.py
D lib/python/asterisk/scenario_iterator.py
A lib/python/asterisk/sipp_iterator.py
D tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/mwi_check.py
M tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/test-config.yaml
D tests/channels/pjsip/subscriptions/mwi/unsolicited/mailbox_count_changes/mwi_check.py
M tests/channels/pjsip/subscriptions/mwi/unsolicited/mailbox_count_changes/test-config.yaml
7 files changed, 439 insertions(+), 339 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/testsuite refs/changes/17/19517/1

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

-- 
To view, visit https://gerrit.asterisk.org/c/testsuite/+/19517
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: testsuite
Gerrit-Branch: 16
Gerrit-Change-Id: I57ed85d2a2a044207da23a750e433dcc38eb93ff
Gerrit-Change-Number: 19517
Gerrit-PatchSet: 1
Gerrit-Owner: Michael Bradeen <mbradeen at sangoma.com>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20221101/f30cf147/attachment-0001.html>


More information about the asterisk-code-review mailing list