[Asterisk-code-review] testsuite: NOTIFY count tests unstable (testsuite[20])
Friendly Automation
asteriskteam at digium.com
Thu Sep 15 13:39:04 CDT 2022
Friendly Automation has submitted this change. ( https://gerrit.asterisk.org/c/testsuite/+/19219 )
Change subject: testsuite: NOTIFY count tests unstable
......................................................................
testsuite: NOTIFY count tests unstable
scenario_iterator - new asterisk testsuite py module to allow
iteration of tests that require a series of sipp scenario(s)
+ ami event(s) pairs. Each iteration is started at the stop
of the previous to prevent collision.
mwi_aggregate and mailbox_count_changes - modified to use
this new method
ASTERISK-30214
Change-Id: Ic7f318bc953416ff8d8673ed4768fef264046fe3
---
A lib/python/asterisk/scenario_iterator.py
M tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/mwi_check.py
M tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/test-config.yaml
M 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
5 files changed, 329 insertions(+), 73 deletions(-)
Approvals:
George Joseph: Looks good to me, approved
Friendly Automation: Approved for Submit
diff --git a/lib/python/asterisk/scenario_iterator.py b/lib/python/asterisk/scenario_iterator.py
new file mode 100644
index 0000000..cff1af3
--- /dev/null
+++ b/lib/python/asterisk/scenario_iterator.py
@@ -0,0 +1,231 @@
+'''
+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/tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/mwi_check.py b/tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/mwi_check.py
index 9a5d5e3..9fadeb0 100644
--- a/tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/mwi_check.py
+++ b/tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/mwi_check.py
@@ -8,32 +8,39 @@
the GNU General Public License Version 2.
"""
+import mailbox
import sys
import logging
sys.path.append("lib/python")
-from twisted.internet import reactor
+from asterisk.scenario_iterator import multiIterator
LOGGER = logging.getLogger(__name__)
-mwis = [
- {'mailbox': 'mailbox_a', 'new': '2', 'old': '1'},
- {'mailbox': 'mailbox_b', 'new': '3', 'old': '3'},
+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'}
]
-def walk_states(test_object, accounts):
+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'} ]}
+]
- testami = test_object.ami[0]
- statedelay = 2
- for mwi in mwis:
- LOGGER.info("Sending MWI update. new: %s, old %s" %
- (mwi['new'],
- mwi['old']))
- message = {
- 'Action': 'MWIUpdate',
- 'Mailbox': mwi['mailbox'],
- 'NewMessages': mwi['new'],
- 'OldMessages': mwi['old']
- }
- reactor.callLater(statedelay, testami.sendMessage, message)
- statedelay += 1
+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 48a0971..cabe5e2 100644
--- a/tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/test-config.yaml
+++ b/tests/channels/pjsip/subscriptions/mwi/mwi_aggregate/test-config.yaml
@@ -1,5 +1,4 @@
testinfo:
- skip: 'Unstable - ASTERISK-30214'
summary: 'Ensures mailbox state is aggregated or not aggregated when appropriate'
description: |
"Alice and Bob both receive mailbox updates for mailbox_a and mailbox_b. However, Alice
@@ -30,13 +29,18 @@
test-object:
config-section: sipp-config
typename: 'sipp.SIPpTestCase'
+ modules:
+ -
+ config-section: 'ami-config'
+ typename: 'pluggable_modules.EventActionModule'
sipp-config:
connect-ami: 'True'
reactor-timeout: 30
fail-on-any: True
- start_callback_module: 'mwi_check'
- start_callback_method: 'walk_states'
+ 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
@@ -46,12 +50,12 @@
'ordered-args': {'-aa'} }
- { 'key-args': {'scenario': 'bob-registers.xml', '-p': '5062'},
'ordered-args': {'-aa'} }
- # Combining this with the -m makes the scripts regex conditional on call_number
- -
- scenarios:
- - { 'key-args': {'scenario': 'alice-is-notified-1.xml', '-p': '5061'} }
- - { 'key-args': {'scenario': 'bob-is-notified-1.xml', '-p': '5062'} }
- -
- scenarios:
- - { 'key-args': {'scenario': 'alice-is-notified-2.xml', '-p': '5061'} }
- - { 'key-args': {'scenario': 'bob-is-notified-2.xml', '-p': '5062'} }
+ami-config:
+ -
+ ami-events:
+ conditions:
+ match:
+ Event: 'UserEvent'
+ 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
index f5378aa..28c4ded 100644
--- 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
@@ -6,36 +6,31 @@
sys.path.append("lib/python")
from twisted.internet import reactor
+from asterisk.scenario_iterator import singleIterator
LOGGER = logging.getLogger(__name__)
-mwis = [
- {'new': '2', 'old': '0'},
- {'new': '1', 'old': '1'},
- {'new': '0', 'old': '2'},
+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'}
]
-def walk_states(test_object, junk):
+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'}
+]
- testami = test_object.ami[0]
- statedelay = 2
- for mwi in mwis:
- LOGGER.info("Sending MWI update. new: %s, old %s" %
- (mwi['new'],
- mwi['old']))
- message = {
- 'Action': 'MWIUpdate',
- 'Mailbox': 'alice',
- 'NewMessages': mwi['new'],
- 'OldMessages': mwi['old']
- }
- reactor.callLater(statedelay, testami.sendMessage, message)
- statedelay += .25
+def start_test(test_object, junk):
+ LOGGER.info("Starting mwi_check")
+ testrunner = singleIterator(test_object, mwiscenarios, mwis)
+ testrunner.run(junk)
- # delete mailbox after walking states
- LOGGER.info("Deleting Mailbox")
- message = {
- 'Action': 'MWIDelete',
- 'Mailbox': 'alice',
- }
- reactor.callLater(statedelay, testami.sendMessage, message)
+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 5cb1150..14f9456 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
@@ -1,5 +1,4 @@
testinfo:
- skip: 'See ASTERISK-30214'
summary: 'Ensures MWI bodies consist of accurate information'
description: |
"Unsolicited MWI notifications are sent to an endpoint as mailbox state updates. sipp
@@ -26,13 +25,18 @@
test-object:
config-section: sipp-config
typename: 'sipp.SIPpTestCase'
+ modules:
+ -
+ config-section: 'ami-config'
+ typename: 'pluggable_modules.EventActionModule'
sipp-config:
connect-ami: 'True'
reactor-timeout: 30
fail-on-any: True
- start_callback_module: 'mwi_check'
- start_callback_method: 'walk_states'
+ 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
@@ -40,16 +44,12 @@
scenarios:
- { 'key-args': {'scenario': 'alice-registers.xml', '-p': '5061'},
'ordered-args': {'-aa'} }
- # Combining this with the -m makes the scripts regex conditional on call_number
- -
- scenarios:
- - { 'key-args': {'scenario': 'alice-is-notified-1.xml', '-p': '5061'} }
- -
- scenarios:
- - { 'key-args': {'scenario': 'alice-is-notified-2.xml', '-p': '5061'} }
- -
- scenarios:
- - { 'key-args': {'scenario': 'alice-is-notified-3.xml', '-p': '5061'} }
- -
- scenarios:
- - { 'key-args': {'scenario': 'alice-is-notified-4.xml', '-p': '5061'} }
+ami-config:
+ -
+ ami-events:
+ conditions:
+ match:
+ Event: 'UserEvent'
+ UserEvent: 'testComplete'
+ count: 1
+ stop_test:
--
To view, visit https://gerrit.asterisk.org/c/testsuite/+/19219
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings
Gerrit-Project: testsuite
Gerrit-Branch: 20
Gerrit-Change-Id: Ic7f318bc953416ff8d8673ed4768fef264046fe3
Gerrit-Change-Number: 19219
Gerrit-PatchSet: 1
Gerrit-Owner: Michael Bradeen <mbradeen at sangoma.com>
Gerrit-Reviewer: Friendly Automation
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20220915/b0ba18b7/attachment-0001.html>
More information about the asterisk-code-review
mailing list