[Asterisk-code-review] tests/channels/pjsip/subscriptions/ast restart: New test. (testsuite[master])

Richard Mudgett asteriskteam at digium.com
Mon Mar 6 13:47:31 CST 2017


Richard Mudgett has uploaded a new change for review. ( https://gerrit.asterisk.org/5124 )

Change subject: tests/channels/pjsip/subscriptions/ast_restart: New test.
......................................................................

tests/channels/pjsip/subscriptions/ast_restart: New test.

Test that subscriptions survive an Asterisk restart.

This is the first pluggable test that causes Asterisk to be restarted as
part of the test.  As such, the AMI EventActionModule, TestCase, and the
SIPpTestCase needed some new code to handle an Asterisk restart.

* Added 'allow-ami-reconnects' boolean option to inform the TestCase base
class that AMI must be able to reconnect after Asterisk is restarted
during the test.  The option defaults to False.  The option is only needed
because old versions of starpy did not support reconnecting the AMI
session if the connection is broken for some reason.  The option basically
ensures that the test will fail with a meaningful error message if the
version of starpy does not support reconnects.

* Added support for the ami-start event of the AMI EventActionModule to
distinguish which Asterisk instance AMI got started on.  The optional
instance id defaults to '0'.

ami-config:
    ami-start:
        id: '1'
    ami-actions:

* Added the new ami-restart event of the AMI EventActionModule to trigger
actions.  The ami-restart event happens when the AMI connection is
reconnected and Asterisk is fully booted.  You can optionally specify
which Asterisk instance (defaults to '0') the event applies.  You can
optionally keep track of the 'count' of restart events required for the
test to pass.  You can optionally trigger the action only on the specified
event count.  Otherwise, the actions are triggered on every event.

ami-config:
    ami-restart:
        id: '1'
        count: '>1'
        trigger-on-count: True
    ami-actions:

Change-Id: I545a7921f5d201b26140c00156777c4c0c9e5dd7
---
M lib/python/asterisk/ami.py
M lib/python/asterisk/sipp.py
M lib/python/asterisk/test_case.py
M sample-yaml/test-config.yaml.sample
A tests/channels/pjsip/subscriptions/ast_restart/configs/ast1/extensions.conf
A tests/channels/pjsip/subscriptions/ast_restart/configs/ast1/pjsip.conf
A tests/channels/pjsip/subscriptions/ast_restart/sipp/subscribe.xml
A tests/channels/pjsip/subscriptions/ast_restart/test-config.yaml
M tests/channels/pjsip/subscriptions/tests.yaml
9 files changed, 537 insertions(+), 17 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/testsuite refs/changes/24/5124/1

diff --git a/lib/python/asterisk/ami.py b/lib/python/asterisk/ami.py
index 9d991a0..fb52d1c 100644
--- a/lib/python/asterisk/ami.py
+++ b/lib/python/asterisk/ami.py
@@ -94,10 +94,21 @@
             raise Exception
 
         test_object.register_ami_observer(self.ami_connect)
+        # We have to reregister the AMI event handlers on reconnection
+        # because losing the connection clears all AMI event handlers.
+        test_object.register_ami_reconnect_observer(self._ami_reconnect)
         test_object.register_stop_observer(self.__check_result)
 
     def ami_connect(self, ami):
         """AMI connect handler"""
+        self.register_handler(ami)
+
+    def _ami_reconnect(self, ami):
+        self._registered = False
+        self.ami_reconnect(ami)
+
+    def ami_reconnect(self, ami):
+        """AMI reconnect handler"""
         self.register_handler(ami)
 
     def register_handler(self, ami):
@@ -106,7 +117,7 @@
         Note:
         In general, most objects won't need this method.  You would only call
         this from a derived object when you create instances of the derived
-        object after AMI connect.
+        object after AMI connect/reconnect.
         """
         if str(ami.id) in self.ids and not self._registered:
             LOGGER.debug("Registering event %s",
@@ -673,21 +684,103 @@
 
 
 class AMIStartEventModule(object):
-    """An event module that triggers when the test starts."""
+    """An event module that triggers when AMI initially connects and
+    Asterisk is fully booted."""
 
     def __init__(self, test_object, triggered_callback, config):
-        """Setup the test start observer"""
+        """Setup the AMI connect observer"""
         self.test_object = test_object
         self.triggered_callback = triggered_callback
+        if config is None:
+            # Happens if ami-start has no parameters.
+            config = {}
         self.config = config
+        self.ids = config['id'].split(',') if 'id' in config else ['0']
         test_object.register_ami_observer(self.start_observer)
 
     def start_observer(self, ami):
         """Notify the event-action mapper that ami has started."""
-        self.triggered_callback(self, ami)
+        if str(ami.id) in self.ids:
+            self.triggered_callback(self, ami)
 PLUGGABLE_EVENT_REGISTRY.register("ami-start", AMIStartEventModule)
 
 
+class AMIRestartEventModule(object):
+    """An event module that triggers when AMI reconnects and
+    Asterisk is fully booted."""
+
+    def __init__(self, test_object, triggered_callback, config):
+        """Setup the AMI reconnect observer"""
+        LOGGER.debug("Initializing an AMIRestartEventModule")
+        self.test_object = test_object
+        self.triggered_callback = triggered_callback
+        if config is None:
+            # Happens if ami-restart has no parameters.
+            config = {}
+        self.config = config
+        self.ids = config['id'].split(',') if 'id' in config else ['0']
+        self.trigger_on_count = config.get('trigger-on-count', False)
+
+        # Keep track of how many restarts happened to check if we got the
+        # expected number of events.
+        self.count = {}
+        if 'count' in config:
+            count = config['count']
+            if isinstance(count, int):
+                # Need exactly this many events
+                self.count['min'] = count
+                self.count['max'] = count
+            elif count[0] == '<':
+                # Need at most this many events
+                self.count['min'] = 0
+                self.count['max'] = int(count[1:])
+            elif count[0] == '>':
+                # Need at least this many events
+                self.count['min'] = int(count[1:])
+                self.count['max'] = float("inf")
+            else:
+                # Need exactly this many events
+                self.count['min'] = int(count)
+                self.count['max'] = int(count)
+        else:
+            self.count['min'] = 0
+            self.count['max'] = float("inf")
+
+        self.count['event'] = 0
+
+        test_object.register_ami_reconnect_observer(self.restart_observer)
+        test_object.register_stop_observer(self.__check_result)
+
+    def restart_observer(self, ami):
+        """Notify the event-action mapper that ami has restarted
+        if it is time to trigger the action."""
+        if str(ami.id) in self.ids:
+            self.count['event'] += 1
+            if not self.trigger_on_count or self.count['event'] == self.count['min']:
+                LOGGER.debug("Restart event callback triggered")
+                self.triggered_callback(self, ami)
+
+    def check_result(self, callback_param):
+        """Method that can be overridden by subclasses"""
+        return callback_param
+
+    def __check_result(self, callback_param):
+        """Verify results
+
+        This will check against event counts and the like and then call into
+        overridden versions via check_result
+        """
+        if (self.count['event'] > self.count['max'] or
+                self.count['event'] < self.count['min']):
+            LOGGER.warning("Restart event occurred %d times, which is out"
+                           " of the allowable range", self.count['event'])
+            LOGGER.warning("Restart event description: %s", str(self.config))
+            self.test_object.set_passed(False)
+            return callback_param
+        return self.check_result(callback_param)
+PLUGGABLE_EVENT_REGISTRY.register("ami-restart", AMIRestartEventModule)
+
+
 class AMIPluggableEventInstance(AMIHeaderMatchInstance):
     """Subclass of AMIEventInstance that works with the pluggable event action
     module.
diff --git a/lib/python/asterisk/sipp.py b/lib/python/asterisk/sipp.py
index e6e8fbb..aa0e34b 100644
--- a/lib/python/asterisk/sipp.py
+++ b/lib/python/asterisk/sipp.py
@@ -123,10 +123,11 @@
         Create an AMI factory in case anyone wants it
         """
         super(SIPpTestCase, self).run()
-        self.create_ami_factory()
 
         self.ast[0].cli_exec('sip set debug on')
         self.ast[0].cli_exec('pjsip set logger on')
+
+        self.create_ami_factory()
 
     def stop_asterisk(self):
         """Kill any remaining SIPp scenarios"""
@@ -142,6 +143,13 @@
 
         self._execute_test()
 
+    def ami_reconnect(self, ami):
+        """Handler for the AMI reconnect event"""
+        super(SIPpTestCase, self).ami_reconnect(ami)
+
+        self.ast[0].cli_exec('sip set debug on')
+        self.ast[0].cli_exec('pjsip set logger on')
+
     def register_scenario_started_observer(self, observer):
         """Register a function to be called when a SIPp scenario starts.
 
diff --git a/lib/python/asterisk/test_case.py b/lib/python/asterisk/test_case.py
index 6764749..dd5365b 100644
--- a/lib/python/asterisk/test_case.py
+++ b/lib/python/asterisk/test_case.py
@@ -138,7 +138,8 @@
         self.ast_version = AsteriskVersion()
         self._start_callbacks = []
         self._stop_callbacks = []
-        self._ami_callbacks = []
+        self._ami_connect_callbacks = []
+        self._ami_reconnect_callbacks = []
         self._pcap_callbacks = []
         self._stop_deferred = None
         log_full = True
@@ -156,8 +157,10 @@
             self.ast_conf_options = test_config.get('ast-config-options')
             log_full = test_config.get('log-full', True)
             log_messages = test_config.get('log-messages', True)
+            self.allow_ami_reconnects = test_config.get('allow-ami-reconnects', False)
         else:
             self.ast_conf_options = None
+            self.allow_ami_reconnects = False
 
         os.makedirs(self.testlogdir)
 
@@ -303,7 +306,7 @@
 
         def on_reconnect(login_deferred):
             """Called if the connection is lost and re-made"""
-            login_deferred.addCallbacks(self._ami_connect, self.ami_login_error)
+            login_deferred.addCallbacks(self._ami_reconnect, self.ami_login_error)
 
         for i, ast_config in enumerate(self.get_asterisk_hosts(count)):
             host = ast_config.get('host')
@@ -314,10 +317,17 @@
 
             self.ami.append(None)
             LOGGER.info("Creating AMIFactory %d to %s" % ((i + 1), host))
-            try:
-                ami_factory = manager.AMIFactory(actual_user, actual_secret, i,
-                                                 on_reconnect=on_reconnect)
-            except:
+            if self.allow_ami_reconnects:
+                try:
+                    ami_factory = manager.AMIFactory(actual_user, actual_secret, i,
+                                                     on_reconnect=on_reconnect)
+                except:
+                    LOGGER.error("starpy.manager.AMIFactory doesn't support reconnects")
+                    LOGGER.error("starpy needs to be updated to run this test")
+                    self.passed = False
+                    self.stop_reactor()
+                    return
+            else:
                 ami_factory = manager.AMIFactory(actual_user, actual_secret, i)
             deferred = ami_factory.login(ip=host, port=actual_port)
             deferred.addCallbacks(self._ami_connect, self.ami_login_error)
@@ -562,13 +572,40 @@
         LOGGER.info("AMI Connect instance %s" % (ami.id + 1))
         self.ami[ami.id] = ami
         try:
-            for callback in self._ami_callbacks:
+            for callback in self._ami_connect_callbacks:
                 callback(ami)
             self.ami_connect(ami)
         except:
             LOGGER.error("Exception raised in ami_connect:")
             LOGGER.error(traceback.format_exc())
             self.stop_reactor()
+        return ami
+
+    def ami_reconnect(self, ami):
+        """Virtual method used after create_ami_factory() successfully re-logs into
+        the Asterisk AMI.
+        """
+        pass
+
+    def _ami_reconnect_fully_booted(self, ami, event):
+        """Callback when AMI reconnects and is fully booted"""
+        LOGGER.info("AMI Reconnect instance %s is fully booted" % (ami.id + 1))
+        ami.deregisterEvent('FullyBooted', self._ami_reconnect_fully_booted)
+        try:
+            for callback in self._ami_reconnect_callbacks:
+                callback(ami)
+            self.ami_reconnect(ami)
+        except:
+            LOGGER.error("Exception raised in ami_reconnect:")
+            LOGGER.error(traceback.format_exc())
+            self.stop_reactor()
+        return (ami, event)
+
+    def _ami_reconnect(self, ami):
+        """Callback when AMI initially reconnects"""
+        LOGGER.info("AMI Reconnect instance %s" % (ami.id + 1))
+        self.ami[ami.id] = ami
+        ami.registerEvent('FullyBooted', self._ami_reconnect_fully_booted)
         return ami
 
     def pcap_callback(self, packet):
@@ -671,7 +708,16 @@
         Parameters:
         callback The deferred callback function to be called when AMI connects
         """
-        self._ami_callbacks.append(callback)
+        self._ami_connect_callbacks.append(callback)
+
+    def register_ami_reconnect_observer(self, callback):
+        """Register an observer that will be called when TestCase reconnects with
+        Asterisk over the Manager interface
+
+        Parameters:
+        callback The deferred callback function to be called when AMI reconnects
+        """
+        self._ami_reconnect_callbacks.append(callback)
 
     def create_fail_token(self, message):
         """Add a fail token to the test. If any fail tokens exist at the end of
diff --git a/sample-yaml/test-config.yaml.sample b/sample-yaml/test-config.yaml.sample
index 0ab04a2..a8d0f69 100644
--- a/sample-yaml/test-config.yaml.sample
+++ b/sample-yaml/test-config.yaml.sample
@@ -219,6 +219,13 @@
     config-path: 'tests/foo'
     reactor-timeout: 30
     spawn-after-hangup: True
+    # Indicate that the test is going to be restarting Asterisk so
+    # we will require the AMI connection to get reconnected after the
+    # restart.  Old versions of starpy did not support reconnecting
+    # the AMI session if the connection was broken.  This option
+    # basically ensures that the test will fail with a meaningful
+    # error message if that feature is not supported by starpy.
+    allow-ami-reconnects: True
     # Whether a full log file containg DEBUG level should be created, defaults to True
     log-full: True
     # Whether a messages log file containing INFO level should be created, defaults to True
diff --git a/tests/channels/pjsip/subscriptions/ast_restart/configs/ast1/extensions.conf b/tests/channels/pjsip/subscriptions/ast_restart/configs/ast1/extensions.conf
new file mode 100644
index 0000000..3b6e8a8
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/ast_restart/configs/ast1/extensions.conf
@@ -0,0 +1,7 @@
+[general]
+
+[default]
+
+exten = alice,hint,Custom:hitchcock
+exten = alice,1,Noop()
+same = n,Hangup()
diff --git a/tests/channels/pjsip/subscriptions/ast_restart/configs/ast1/pjsip.conf b/tests/channels/pjsip/subscriptions/ast_restart/configs/ast1/pjsip.conf
new file mode 100644
index 0000000..148bbf6
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/ast_restart/configs/ast1/pjsip.conf
@@ -0,0 +1,25 @@
+[local]
+type=transport
+protocol=udp
+bind=0.0.0.0
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+[endpoint_t](!)
+type=endpoint
+context=default
+direct_media=no
+disallow=all
+allow=ulaw
+
+[aor_t](!)
+type=aor
+max_contacts=1
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+[alice](aor_t)
+contact=sip:alice at 127.0.0.1:5061
+
+[alice](endpoint_t)
+aors=alice
diff --git a/tests/channels/pjsip/subscriptions/ast_restart/sipp/subscribe.xml b/tests/channels/pjsip/subscriptions/ast_restart/sipp/subscribe.xml
new file mode 100644
index 0000000..e87351a
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/ast_restart/sipp/subscribe.xml
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<scenario name="Subscribe">
+	<send retrans="500">
+		<![CDATA[
+			SUBSCRIBE sip:alice@[remote_ip]:[remote_port] SIP/2.0
+			Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+			From: "alice" <sip:alice@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
+			To: <sip:alice@[remote_ip]:[remote_port]>
+			Call-ID: [call_id]
+			CSeq: 1 SUBSCRIBE
+			Contact: "alice" <sip:alice@[local_ip]:[local_port]>
+			Expires: 300
+			Max-Forwards: 70
+			Event: presence
+			Accept: application/pidf+xml
+			User-Agent: Digium D40
+			Content-Length: 0
+		]]>
+	</send>
+
+	<recv response="200" rtd="true">
+		<action>
+			<ereg regexp="(;tag=.*)" header="To:" search_in="hdr" check_it="true" assign_to="to_tag"/>
+		</action>
+	</rcv>
+
+	<!-- NOTIFY for the initial subscription -->
+	<recv request="NOTIFY" crlf="true">
+		<action>
+			<ereg regexp="active;expires=[2,3][0,9][0,5-9]" check_it="true" search_in="hdr" header="Subscription-State" assign_to="substate" />
+		</action>
+	</recv>
+
+	<send>
+		<![CDATA[
+			SIP/2.0 200 OK
+			[last_Via:]
+			[last_From:]
+			[last_To:]
+			[last_Call-ID:]
+			[last_CSeq:]
+			Contact: <sip:[local_ip]:[local_port];transport=[transport]>
+			Content-Length: 0
+		]]>
+	</send>
+
+	<!-- NOTIFY for Asterisk recreating the subscription -->
+	<recv request="NOTIFY" crlf="true">
+	</recv>
+
+	<send>
+		<![CDATA[
+			SIP/2.0 200 OK
+			[last_Via:]
+			[last_From:]
+			[last_To:]
+			[last_Call-ID:]
+			[last_CSeq:]
+			Contact: <sip:[local_ip]:[local_port];transport=[transport]>
+			Content-Length: 0
+		]]>
+	</send>
+
+	<!-- NOTIFY for AMI reconnecting and then changing the device state  -->
+	<recv request="NOTIFY" crlf="true">
+	</recv>
+
+	<send>
+		<![CDATA[
+			SIP/2.0 200 OK
+			[last_Via:]
+			[last_From:]
+			[last_To:]
+			[last_Call-ID:]
+			[last_CSeq:]
+			Contact: <sip:[local_ip]:[local_port];transport=[transport]>
+			Content-Length: 0
+		]]>
+	</send>
+
+	<!-- Refresh subscription  -->
+	<send retrans="500">
+		<![CDATA[
+			SUBSCRIBE sip:alice@[remote_ip]:[remote_port] SIP/2.0
+			Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+			From: "alice" <sip:alice@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
+			To: <sip:alice@[remote_ip]:[remote_port]>[$to_tag]
+			Call-ID: [call_id]
+			CSeq: 2 SUBSCRIBE
+			Contact: "alice" <sip:alice@[local_ip]:[local_port]>
+			Expires: 600
+			Max-Forwards: 70
+			Event: presence
+			Accept: application/pidf+xml
+			User-Agent: Digium D40
+			Content-Length: 0
+		]]>
+	</send>
+
+	<recv response="200" rtd="true" />
+
+	<!-- NOTIFY for the subscription refresh -->
+	<recv request="NOTIFY" crlf="true">
+		<action>
+			<ereg regexp="active;expires=[5,6][0,9][0,5-9]" check_it="true" search_in="hdr" header="Subscription-State" assign_to="resubstate" />
+		</action>
+	</recv>
+
+	<send>
+		<![CDATA[
+			SIP/2.0 200 OK
+			[last_Via:]
+			[last_From:]
+			[last_To:]
+			[last_Call-ID:]
+			[last_CSeq:]
+			Contact: <sip:[local_ip]:[local_port];transport=[transport]>
+			Content-Length: 0
+		]]>
+	</send>
+
+	<!-- NOTIFY for AMI changing the device state again  -->
+	<recv request="NOTIFY" crlf="true">
+	</recv>
+
+	<send>
+		<![CDATA[
+			SIP/2.0 200 OK
+			[last_Via:]
+			[last_From:]
+			[last_To:]
+			[last_Call-ID:]
+			[last_CSeq:]
+			Contact: <sip:[local_ip]:[local_port];transport=[transport]>
+			Content-Length: 0
+		]]>
+	</send>
+
+	<!-- Unsubscribe  -->
+	<send retrans="500">
+		<![CDATA[
+			SUBSCRIBE sip:alice@[remote_ip]:[remote_port] SIP/2.0
+			Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+			From: "alice" <sip:alice@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
+			To: <sip:alice@[remote_ip]:[remote_port]>[$to_tag]
+			Call-ID: [call_id]
+			CSeq: 3 SUBSCRIBE
+			Contact: "alice" <sip:alice@[local_ip]:[local_port]>
+			Expires: 0
+			Max-Forwards: 70
+			Event: presence
+			Accept: application/pidf+xml
+			User-Agent: Digium D40
+			Content-Length: 0
+		]]>
+	</send>
+
+	<recv response="200" rtd="true" />
+
+	<!-- NOTIFY for the subscription termination -->
+	<recv request="NOTIFY" crlf="true">
+		<action>
+			<ereg regexp="terminated" check_it="true" search_in="hdr" header="Subscription-State" assign_to="unsubstate" />
+		</action>
+	</recv>
+
+	<send>
+		<![CDATA[
+			SIP/2.0 200 OK
+			[last_Via:]
+			[last_From:]
+			[last_To:]
+			[last_Call-ID:]
+			[last_CSeq:]
+			Contact: <sip:[local_ip]:[local_port];transport=[transport]>
+			Content-Length: 0
+		]]>
+	</send>
+
+	<Reference variables="substate,resubstate,unsubstate" />
+
+</scenario>
diff --git a/tests/channels/pjsip/subscriptions/ast_restart/test-config.yaml b/tests/channels/pjsip/subscriptions/ast_restart/test-config.yaml
new file mode 100644
index 0000000..80470ed
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/ast_restart/test-config.yaml
@@ -0,0 +1,149 @@
+testinfo:
+    summary: 'Test subscription persistence over Asterisk restarts'
+    description: |
+        Tests that subscriptions survive an Asterisk restart.
+        The test ping-pongs between the SIPp scenario and Asterisk
+        by waiting for the other to signal completion of key events.
+        1) A SIPp scenario requests a subscription.
+        2) Asterisk is restarted when the subscription gets established.
+        3) Asterisk changes the extension custom device state after the
+        restart recreates the subscription.
+        4) The SIPp scenario refreshes the subscription.
+        5) Asterisk changes the extension custom device state again after
+        the subscription is refreshed.
+        6) The SIPp scenario unsubscribes to complete the scenario.
+        7) The test completes successfully when Asterisk sees the
+        subscription get terminated.
+
+properties:
+    minversion: [ '13.14.0', '14.3.0' ]
+    dependencies:
+        - buildoption: 'TEST_FRAMEWORK'
+        - python : 'twisted'
+        - python : 'starpy'
+        - sipp :
+            version : 'v3.0'
+        - asterisk : 'func_devstate'
+        - asterisk : 'res_pjsip'
+        - asterisk : 'res_pjsip_pubsub'
+    tags:
+        - pjsip
+
+test-modules:
+    test-object:
+        config-section: sipp-config
+        typename: 'sipp.SIPpTestCase'
+    modules:
+        -
+            config-section: 'ami-config'
+            typename: 'pluggable_modules.EventActionModule'
+
+sipp-config:
+    stop-after-scenarios: False
+    fail-on-any: True
+
+    # Indicate that the test is going to be restarting Asterisk so
+    # we will require the AMI connection to get reconnected after the
+    # restart.  Old versions of starpy did not support reconnecting
+    # the AMI session if the connection was broken.  This option
+    # basically ensures that the test will fail with a meaningful
+    # error message if that feature is not supported by starpy.
+    allow-ami-reconnects: True
+
+    test-iterations:
+        -
+            scenarios:
+                - { 'key-args': {'scenario': 'subscribe.xml', '-p': '5061'} }
+
+ami-config:
+    -
+        # Event generated on initial subscription creation
+        # and on subscription recreation after a restart.
+        ami-events:
+            conditions:
+                match:
+                    Event: 'TestEvent'
+                    State: 'SUBSCRIPTION_ESTABLISHED'
+            requirements:
+                match:
+                    Resource: 'alice'
+            # Trigger on the first event
+            count: '>1'
+            trigger-on-count: True
+        ami-actions:
+            action:
+                Action: 'Command'
+                ActionID: '12345'
+                Command: 'core restart gracefully'
+    -
+        # Wake up after Asterisk is fully booted again.
+        # This tends to be too early for the restart NOTIFY to have
+        # a chance to go out.  Recreating persistent subscriptions
+        # has to wait for Asterisk to be fully booted to ensure that
+        # all pjsip modules are loaded and ready.
+        # We'll just require that we get restarted once.
+        ami-restart:
+            count: '1'
+    -
+        ami-events:
+            conditions:
+                match:
+                    Event: 'TestEvent'
+                    State: 'SUBSCRIPTION_ESTABLISHED'
+            requirements:
+                match:
+                    Resource: 'alice'
+            # Trigger on the second event
+            count: '2'
+            trigger-on-count: True
+        ami-actions:
+            action:
+                Action: 'SetVar'
+                ActionID: '23456'
+                Variable: 'DEVICE_STATE(Custom:hitchcock)'
+                Value: 'BUSY'
+    -
+        ami-events:
+            conditions:
+                match:
+                    Event: 'TestEvent'
+                    State: 'SUBSCRIPTION_REFRESHED'
+            requirements:
+                match:
+                    Resource: 'alice'
+            count: '1'
+            trigger-on-count: True
+        ami-actions:
+            # Changing the custom device state again so we can re-run
+            # the test without a previous run possibly interfering.
+            # This is basically paranoia because I think the AstDB is
+            # deleted between tests.  However, refreshing the subscription
+            # further tests that the subscription dialog is fully
+            # recreated after the restart.
+            action:
+                Action: 'SetVar'
+                ActionID: '34567'
+                Variable: 'DEVICE_STATE(Custom:hitchcock)'
+                Value: 'NOT_INUSE'
+    -
+        ami-events:
+            conditions:
+                match:
+                    Event: 'TestEvent'
+                    State: 'SUBSCRIPTION_STATE_CHANGED'
+            requirements:
+                match:
+                    Resource: 'alice'
+            # Changed the custom device state twice.
+            count: '2'
+    -
+        ami-events:
+            conditions:
+                match:
+                    Event: 'TestEvent'
+                    State: 'SUBSCRIPTION_TERMINATED'
+            requirements:
+                match:
+                    Resource: 'alice'
+            count: '1'
+        stop_test:
diff --git a/tests/channels/pjsip/subscriptions/tests.yaml b/tests/channels/pjsip/subscriptions/tests.yaml
index e44b7ca..deebc2a 100644
--- a/tests/channels/pjsip/subscriptions/tests.yaml
+++ b/tests/channels/pjsip/subscriptions/tests.yaml
@@ -2,11 +2,12 @@
 tests:
     - dir: 'mwi'
     - dir: 'presence'
-    - test: 'no_event_header'
-    - test: 'unknown_event_package'
-    - test: 'unallowed'
-    - test: 'below_min_expiry'
     - dir: 'rls'
+    - test: 'ast_restart'
+    - test: 'below_min_expiry'
     - test: 'mismatch'
     - test: 'nat_notify'
+    - test: 'no_event_header'
     - test: 'subscribe_context'
+    - test: 'unallowed'
+    - test: 'unknown_event_package'

-- 
To view, visit https://gerrit.asterisk.org/5124
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I545a7921f5d201b26140c00156777c4c0c9e5dd7
Gerrit-PatchSet: 1
Gerrit-Project: testsuite
Gerrit-Branch: master
Gerrit-Owner: Richard Mudgett <rmudgett at digium.com>



More information about the asterisk-code-review mailing list