[Asterisk-code-review] pjsip mwi: Add a few more tests for solicited mwi subscriptinons (...testsuite[17])

Friendly Automation asteriskteam at digium.com
Tue Sep 10 07:47:22 CDT 2019


Friendly Automation has submitted this change and it was merged. ( https://gerrit.asterisk.org/c/testsuite/+/12821 )

Change subject: pjsip mwi: Add a few more tests for solicited mwi subscriptinons
......................................................................

pjsip mwi: Add a few more tests for solicited mwi subscriptinons

Added some tests to ensure that the "mwi_subscribe_replaces_unsolicited" option
works as it should. The tests make sure aggregation is also handled correctly
when enabled.

A couple tests make sure that a solicited subscription is able to replace
an unsolicited subscription when appropriate. A couple of other tests make
sure that if the unsolicited is created after the solicited one then the
solicited subscription is shutdown, and the unsolicited one is allowed.

Lastly, one test makes sure that when a solicited subscription replaces an
unsolicited one then when the contact is updated no extra MWI messages are
sent out.

ASTERISK-28488

Change-Id: I50ebd212789249f14056b8b3948304237a5f1042
---
A tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/aggregate/configs/ast1/pjsip.conf
A tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/aggregate/sipp/solicited.xml
A tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/aggregate/sipp/unsolicited.xml
A tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/aggregate/test-config.yaml
A tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/non_aggregate/configs/ast1/pjsip.conf
A tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/non_aggregate/sipp/solicited.xml
A tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/non_aggregate/sipp/unsolicited.xml
A tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/non_aggregate/test-config.yaml
A tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/tests.yaml
A tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/aggregate/configs/ast1/pjsip.conf
A tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/aggregate/sipp/solicited.xml
A tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/aggregate/sipp/unsolicited.xml
A tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/aggregate/test-config.yaml
A tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/multiple_registers/configs/ast1/pjsip.conf
A tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/multiple_registers/sipp/solicited.xml
A tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/multiple_registers/sipp/unsolicited.xml
A tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/multiple_registers/test-config.yaml
A tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/non_aggregate/configs/ast1/pjsip.conf
A tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/non_aggregate/sipp/solicited.xml
A tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/non_aggregate/sipp/unsolicited.xml
A tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/non_aggregate/test-config.yaml
A tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/tests.yaml
A tests/channels/pjsip/subscriptions/mwi/solicited/tests.yaml
M tests/channels/pjsip/subscriptions/mwi/tests.yaml
24 files changed, 1,454 insertions(+), 0 deletions(-)

Approvals:
  Joshua Colp: Looks good to me, but someone else must approve
  George Joseph: Looks good to me, approved
  Friendly Automation: Approved for Submit



diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/aggregate/configs/ast1/pjsip.conf b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/aggregate/configs/ast1/pjsip.conf
new file mode 100644
index 0000000..5aeab58
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/aggregate/configs/ast1/pjsip.conf
@@ -0,0 +1,29 @@
+[global]
+debug=yes
+mwi_disable_initial_unsolicited=yes
+
+[transport-template](!)
+type=transport
+bind=127.0.0.1:5060
+
+[transport-udp](transport-template)
+protocol=udp
+
+[aor-template](!)
+type=aor
+max_contacts=1
+
+[endpoint-template](!)
+type=endpoint
+context=default
+allow=!all,ulaw
+direct_media=false
+
+[alice](aor-template)
+mailboxes=mb1
+
+[alice](endpoint-template)
+aors=alice
+mailboxes=mb1,mb2
+mwi_subscribe_replaces_unsolicited=no
+aggregate_mwi=yes
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/aggregate/sipp/solicited.xml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/aggregate/sipp/solicited.xml
new file mode 100644
index 0000000..088ff90
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/aggregate/sipp/solicited.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<scenario name="Solicited">
+
+  <!-- Wait a few seconds before initiating the scenario to make sure
+	   the mailbox(es) have been updated -->
+  <pause milliseconds="3000" />
+
+  <!-- Setup the solicited subscription first -->
+  <send retrans="500">
+    <![CDATA[
+      SUBSCRIBE sip:[service]@[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=[call_number]
+      To: "alice" <sip:[service]@[remote_ip]:[remote_port]>
+      Call-ID: [call_id]
+      CSeq: 1 SUBSCRIBE
+      Contact: "alice" <sip:alice@[local_ip]:[local_port]>
+      Max-Forwards: 70
+      Event: message-summary
+      Expires: 60
+      Accept: application/simple-message-summary
+      Allow-Events: message-summary
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200" />
+
+  <!-- Receive NOTIFY due to SUBSCRIBE -->
+  <recv request="NOTIFY">
+    <action>
+      <ereg regexp="message-summary" check_it="true" search_in="hdr" header="Event" assign_to="event" />
+      <ereg regexp="active;expires=[5,6][0-9]" check_it="true" search_in="hdr" header="Subscription-State" assign_to="state" />
+      <ereg regexp="Voice-Message: 1\/1" check_it="true" search_in="body" assign_to="vm" />
+    </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>
+
+  <pause milliseconds="1000" />
+
+  <!-- Now by updating the contact an unsolicited subscription will be setup. Since
+	   the mwi_subscribe_replaces_unsolicited option is disabled the solicited
+	   subscription is shutdown. -->
+  <send retrans="500">
+    <![CDATA[
+
+      REGISTER sip:[remote_ip]:[remote_port] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+      From: "[service]" <sip:[service]@[local_ip]:[local_port]>;tag=[call_number]
+      To: "[service]" <sip:[service]@[remote_ip]:[remote_port]>
+      Call-ID: [call_id]
+      CSeq: [cseq] REGISTER
+      Max-Forwards: 70
+      Contact: <sip:[service]@[local_ip]:5062;transport=[transport]>
+      Expires: 60
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200" />
+
+  <!-- Receive NOTIFY due to solicited subscription shutdown -->
+  <recv request="NOTIFY">
+    <action>
+      <ereg regexp="message-summary" check_it="true" search_in="hdr" header="Event" assign_to="event" />
+      <ereg regexp="terminated" check_it="true" search_in="hdr" header="Subscription-State" assign_to="state" />
+      <ereg regexp="Voice-Message: 1\/1" check_it="true" search_in="body" assign_to="vm" />
+    </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="event,state,vm" />
+
+</scenario>
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/aggregate/sipp/unsolicited.xml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/aggregate/sipp/unsolicited.xml
new file mode 100644
index 0000000..f541bb4
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/aggregate/sipp/unsolicited.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<scenario name="Unsolicited">
+
+  <recv request="NOTIFY">
+    <action>
+	  <ereg regexp="message-summary" check_it="true" search_in="hdr" header="Event" assign_to="event" />
+      <ereg regexp="terminated" check_it="true" search_in="hdr" header="Subscription-State" assign_to="state" />
+	  <!-- Expect the combined mailbox information for mb1 and mb2 -->
+      <ereg regexp="Voice-Message: 3\/3" check_it="true" search_in="body" assign_to="vm" />
+	  <!-- Get the call number and see if we've either met or acceded the expected number. -->
+      <assignstr assign_to="call_num" value="[call_number]" />
+      <todouble assign_to="count" variable="call_num" />
+      <test assign_to="expected" variable="count" compare="equal" value="1" />
+      <test assign_to="greater" variable="count" compare="greater_than" value="1" />
+    </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>
+
+  <!-- Fail the scenario if we've received more than the expected number of calls -->
+  <nop condexec="greater">
+    <action>
+      <error message="Received too many out of call NOTIFYs ([call_id])" />
+    </action>
+  </nop>
+
+  <!-- Pause for a time less than the global timeout, but long enough where we feel
+       that we won't potentially receive any erroneous calls after the expected call
+       count has been met. If we stop too soon then the scenario could have successfully
+       ended, but another unexpected message may have potentially arrived, which in
+       that case we should have failed the test. -->
+  <pause milliseconds="10000" />
+
+  <!-- Stop the scenario gracefully if we've received the expected number of calls
+	   Note, do this stops the test in a non-failure mode. -->
+  <nop condexec="expected">
+    <action>
+      <log message="call count met stopping test" />
+      <exec int_cmd="stop_gracefully" />
+    </action>
+  </nop>
+
+  <Reference variables="event,state,vm" />
+
+</scenario>
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/aggregate/test-config.yaml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/aggregate/test-config.yaml
new file mode 100644
index 0000000..13a48a9
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/aggregate/test-config.yaml
@@ -0,0 +1,72 @@
+testinfo:
+    summary: 'Test a solicited subscription getting replaced by an unsolicited one.'
+    description: |
+        'This tests that when the mwi_subscribe_replaces_unsolicited option is
+        disabled (set to "no"), and a contact is updated, thus setting up the
+        unsolicited subscription, the solicited subscription is properly
+        terminated. As well this verifies that the unsolicited subscription
+        gets setup.
+
+        Note, this tests disables initial unsolicited at startup in order to
+        make the test a bit more deterministic.'
+
+properties:
+    dependencies:
+        - python: 'twisted'
+        - python: 'starpy'
+        - asterisk: 'res_pjsip'
+        - asterisk: 'res_pjsip_mwi'
+        - asterisk: 'res_mwi_external'
+        - asterisk: 'res_mwi_external_ami'
+        - sipp:
+            version: 'v3.0'
+    tags:
+        - pjsip
+
+test-modules:
+    test-object:
+        config-section: 'sipp-config'
+        typename: 'sipp.SIPpTestCase'
+    modules:
+        -
+            config-section: 'event-action-config'
+            typename: 'pluggable_modules.EventActionModule'
+
+sipp-config:
+    test-iterations:
+        -
+            # Due to the nature of SIPp, and it's handling of out of call messages
+            # we'll execute two scenarios. One to receive unsolicited MWI, and
+            # another to handle solicited MWI.
+            scenarios:
+                # For this test we expect to receive 1 unsolicited MWI message.
+                # One single message is sent with combined mailbox information for
+                # "mb1" and one for "mb2". This occurs when unsolicited is setup.
+                #
+                # Even though we expect only 1 unsolicited message set the call count
+                # to 2 for the scenario. Once the expected call count is met the scenario
+                # is responsible for gracefully shutting itself down. See the scenario
+                # itself for more information.
+                #
+                # Note, we must tell SIPp to cause an error on scenario timeout (20 seconds).
+                # This way the test will fail if the expected number of calls is not met.
+                - { 'key-args': {'scenario': 'unsolicited.xml', '-i': '127.0.0.1', '-p': '5062', '-m': '2', '-timeout_error': ''} }
+                - { 'key-args': {'scenario': 'solicited.xml', '-i': '127.0.0.1', '-p': '5061', '-s': 'alice'} }
+
+event-action-config:
+    -
+        ami-start:
+        ami-actions:
+            action:
+                Action: 'MWIUpdate'
+                Mailbox: 'mb1'
+                NewMessages: 1
+                OldMessages: 1
+    -
+        ami-start:
+        ami-actions:
+            action:
+                Action: 'MWIUpdate'
+                Mailbox: 'mb2'
+                NewMessages: 2
+                OldMessages: 2
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/non_aggregate/configs/ast1/pjsip.conf b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/non_aggregate/configs/ast1/pjsip.conf
new file mode 100644
index 0000000..87cdfea
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/non_aggregate/configs/ast1/pjsip.conf
@@ -0,0 +1,29 @@
+[global]
+debug=yes
+mwi_disable_initial_unsolicited=yes
+
+[transport-template](!)
+type=transport
+bind=127.0.0.1:5060
+
+[transport-udp](transport-template)
+protocol=udp
+
+[aor-template](!)
+type=aor
+max_contacts=1
+
+[endpoint-template](!)
+type=endpoint
+context=default
+allow=!all,ulaw
+direct_media=false
+
+[alice](aor-template)
+mailboxes=mb1
+
+[alice](endpoint-template)
+aors=alice
+mailboxes=mb1,mb2
+mwi_subscribe_replaces_unsolicited=no
+aggregate_mwi=no
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/non_aggregate/sipp/solicited.xml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/non_aggregate/sipp/solicited.xml
new file mode 100644
index 0000000..088ff90
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/non_aggregate/sipp/solicited.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<scenario name="Solicited">
+
+  <!-- Wait a few seconds before initiating the scenario to make sure
+	   the mailbox(es) have been updated -->
+  <pause milliseconds="3000" />
+
+  <!-- Setup the solicited subscription first -->
+  <send retrans="500">
+    <![CDATA[
+      SUBSCRIBE sip:[service]@[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=[call_number]
+      To: "alice" <sip:[service]@[remote_ip]:[remote_port]>
+      Call-ID: [call_id]
+      CSeq: 1 SUBSCRIBE
+      Contact: "alice" <sip:alice@[local_ip]:[local_port]>
+      Max-Forwards: 70
+      Event: message-summary
+      Expires: 60
+      Accept: application/simple-message-summary
+      Allow-Events: message-summary
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200" />
+
+  <!-- Receive NOTIFY due to SUBSCRIBE -->
+  <recv request="NOTIFY">
+    <action>
+      <ereg regexp="message-summary" check_it="true" search_in="hdr" header="Event" assign_to="event" />
+      <ereg regexp="active;expires=[5,6][0-9]" check_it="true" search_in="hdr" header="Subscription-State" assign_to="state" />
+      <ereg regexp="Voice-Message: 1\/1" check_it="true" search_in="body" assign_to="vm" />
+    </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>
+
+  <pause milliseconds="1000" />
+
+  <!-- Now by updating the contact an unsolicited subscription will be setup. Since
+	   the mwi_subscribe_replaces_unsolicited option is disabled the solicited
+	   subscription is shutdown. -->
+  <send retrans="500">
+    <![CDATA[
+
+      REGISTER sip:[remote_ip]:[remote_port] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+      From: "[service]" <sip:[service]@[local_ip]:[local_port]>;tag=[call_number]
+      To: "[service]" <sip:[service]@[remote_ip]:[remote_port]>
+      Call-ID: [call_id]
+      CSeq: [cseq] REGISTER
+      Max-Forwards: 70
+      Contact: <sip:[service]@[local_ip]:5062;transport=[transport]>
+      Expires: 60
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200" />
+
+  <!-- Receive NOTIFY due to solicited subscription shutdown -->
+  <recv request="NOTIFY">
+    <action>
+      <ereg regexp="message-summary" check_it="true" search_in="hdr" header="Event" assign_to="event" />
+      <ereg regexp="terminated" check_it="true" search_in="hdr" header="Subscription-State" assign_to="state" />
+      <ereg regexp="Voice-Message: 1\/1" check_it="true" search_in="body" assign_to="vm" />
+    </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="event,state,vm" />
+
+</scenario>
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/non_aggregate/sipp/unsolicited.xml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/non_aggregate/sipp/unsolicited.xml
new file mode 100644
index 0000000..2e5aaad
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/non_aggregate/sipp/unsolicited.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<scenario name="Unsolicited">
+
+  <recv request="NOTIFY">
+    <action>
+	  <ereg regexp="message-summary" check_it="true" search_in="hdr" header="Event" assign_to="event" />
+      <ereg regexp="terminated" check_it="true" search_in="hdr" header="Subscription-State" assign_to="state" />
+      <ereg regexp="Voice-Message: [1|2]\/[1|2]" check_it="true" search_in="body" assign_to="vm" />
+	  <!-- Get the call number and see if we've either met or acceded the expected number. -->
+      <assignstr assign_to="call_num" value="[call_number]" />
+      <todouble assign_to="count" variable="call_num" />
+      <test assign_to="expected" variable="count" compare="equal" value="2" />
+      <test assign_to="greater" variable="count" compare="greater_than" value="2" />
+    </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>
+
+  <!-- Fail the scenario if we've received more than the expected number of calls -->
+  <nop condexec="greater">
+    <action>
+      <error message="Received too many out of call NOTIFYs ([call_id])" />
+    </action>
+  </nop>
+
+  <!-- Pause for a time less than the global timeout, but long enough where we feel
+       that we won't potentially receive any erroneous calls after the expected call
+       count has been met. If we stop too soon then the scenario could have successfully
+       ended, but another unexpected message may have potentially arrived, which in
+       that case we should have failed the test. -->
+  <pause milliseconds="10000" />
+
+  <!-- Stop the scenario gracefully if we've received the expected number of calls
+	   Note, do this stops the test in a non-failure mode. -->
+  <nop condexec="expected">
+    <action>
+      <log message="call count met stopping test" />
+      <exec int_cmd="stop_gracefully" />
+    </action>
+  </nop>
+
+  <Reference variables="event,state,vm" />
+
+</scenario>
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/non_aggregate/test-config.yaml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/non_aggregate/test-config.yaml
new file mode 100644
index 0000000..d875013
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/non_aggregate/test-config.yaml
@@ -0,0 +1,72 @@
+testinfo:
+    summary: 'Test a solicited subscription getting replaced by an unsolicited one.'
+    description: |
+        'This tests that when the mwi_subscribe_replaces_unsolicited option is
+        disabled (set to "no"), and a contact is updated, thus setting up the
+        unsolicited subscription, the solicited subscription is properly
+        terminated. As well this verifies that the unsolicited subscription
+        gets setup.
+
+        Note, this tests disables initial unsolicited at startup in order to
+        make the test a bit more deterministic.'
+
+properties:
+    dependencies:
+        - python: 'twisted'
+        - python: 'starpy'
+        - asterisk: 'res_pjsip'
+        - asterisk: 'res_pjsip_mwi'
+        - asterisk: 'res_mwi_external'
+        - asterisk: 'res_mwi_external_ami'
+        - sipp:
+            version: 'v3.0'
+    tags:
+        - pjsip
+
+test-modules:
+    test-object:
+        config-section: 'sipp-config'
+        typename: 'sipp.SIPpTestCase'
+    modules:
+        -
+            config-section: 'event-action-config'
+            typename: 'pluggable_modules.EventActionModule'
+
+sipp-config:
+    test-iterations:
+        -
+            # Due to the nature of SIPp, and it's handling of out of call messages
+            # we'll execute two scenarios. One to receive unsolicited MWI, and
+            # another to handle solicited MWI.
+            scenarios:
+                # For this test we expect to receive 2 unsolicited MWI messages.
+                # One for "mb1" and one for "mb2". Both occur when unsolicited is
+                # being setup post REGISTER (contact update).
+                #
+                # Even though we expect only 2 unsolicited messages set the call count
+                # to 3 for the scenario. Once the expected call count is met the scenario
+                # is responsible for gracefully shutting itself down. See the scenario
+                # itself for more information.
+                #
+                # Note, we must tell SIPp to cause an error on scenario timeout (20 seconds).
+                # This way the test will fail if the expected number of calls is not met.
+                - { 'key-args': {'scenario': 'unsolicited.xml', '-i': '127.0.0.1', '-p': '5062', '-m': '3', '-timeout_error': ''} }
+                - { 'key-args': {'scenario': 'solicited.xml', '-i': '127.0.0.1', '-p': '5061', '-s': 'alice'} }
+
+event-action-config:
+    -
+        ami-start:
+        ami-actions:
+            action:
+                Action: 'MWIUpdate'
+                Mailbox: 'mb1'
+                NewMessages: 1
+                OldMessages: 1
+    -
+        ami-start:
+        ami-actions:
+            action:
+                Action: 'MWIUpdate'
+                Mailbox: 'mb2'
+                NewMessages: 2
+                OldMessages: 2
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/tests.yaml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/tests.yaml
new file mode 100644
index 0000000..79bdfe0
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaced_by_unsolicited/tests.yaml
@@ -0,0 +1,4 @@
+# Enter tests here in the order they should be considered for execution:
+tests:
+    - test: 'aggregate'
+    - test: 'non_aggregate'
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/aggregate/configs/ast1/pjsip.conf b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/aggregate/configs/ast1/pjsip.conf
new file mode 100644
index 0000000..5b1f463
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/aggregate/configs/ast1/pjsip.conf
@@ -0,0 +1,29 @@
+[global]
+debug=yes
+mwi_disable_initial_unsolicited=yes
+
+[transport-template](!)
+type=transport
+bind=127.0.0.1:5060
+
+[transport-udp](transport-template)
+protocol=udp
+
+[aor-template](!)
+type=aor
+max_contacts=1
+
+[endpoint-template](!)
+type=endpoint
+context=default
+allow=!all,ulaw
+direct_media=false
+
+[alice](aor-template)
+mailboxes=mb1
+
+[alice](endpoint-template)
+aors=alice
+mailboxes=mb1,mb2
+mwi_subscribe_replaces_unsolicited=yes
+aggregate_mwi=yes
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/aggregate/sipp/solicited.xml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/aggregate/sipp/solicited.xml
new file mode 100644
index 0000000..cb0d96e
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/aggregate/sipp/solicited.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<scenario name="Solicited">
+
+  <!-- Wait a few seconds before initiating the scenario to make sure
+	   the mailbox(es) have been updated -->
+  <pause milliseconds="3000" />
+
+  <!-- Once the contact is registered a SIP NOTIFY (for unsolicited MWI) is sent to
+	   the contact. Note, this registers the contact to port 5062 where SIP NOTIFY's
+	   for unsolicited MWI are expected -->
+  <send retrans="500">
+    <![CDATA[
+
+      REGISTER sip:[remote_ip]:[remote_port] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+      From: "[service]" <sip:[service]@[local_ip]:[local_port]>;tag=[call_number]
+      To: "[service]" <sip:[service]@[remote_ip]:[remote_port]>
+      Call-ID: [call_id]
+      CSeq: [cseq] REGISTER
+      Max-Forwards: 70
+      Contact: <sip:[service]@[local_ip]:5062;transport=[transport]>
+      Expires: 60
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200" />
+
+  <!-- Wait a few more seconds to make sure the unsolicited NOTIFY is received
+	   before subscribing -->
+  <pause milliseconds="3000" />
+
+  <!-- Now subscribe -->
+  <send retrans="500">
+    <![CDATA[
+      SUBSCRIBE sip:[service]@[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=[call_number]
+      To: "alice" <sip:[service]@[remote_ip]:[remote_port]>
+      Call-ID: [call_id]
+      CSeq: 1 SUBSCRIBE
+      Contact: "alice" <sip:alice@[local_ip]:[local_port]>
+      Max-Forwards: 70
+      Event: message-summary
+      Expires: 60
+      Accept: application/simple-message-summary
+      Allow-Events: message-summary
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200">
+    <action>
+          <!-- Get the tag to use when unsubscribing -->
+          <ereg regexp="(;tag=.*)" header="To:" search_in="hdr" check_it="true" assign_to="to_tag"/>
+    </action>
+  </recv>
+
+  <!-- Receive NOTIFY due to SUBSCRIBE -->
+  <recv request="NOTIFY">
+    <action>
+      <ereg regexp="message-summary" check_it="true" search_in="hdr" header="Event" assign_to="event" />
+      <ereg regexp="active;expires=[5,6][0-9]" check_it="true" search_in="hdr" header="Subscription-State" assign_to="state" />
+      <ereg regexp="Voice-Message: 1\/1" check_it="true" search_in="body" assign_to="vm" />
+    </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>
+
+  <pause milliseconds="1000" />
+
+  <!-- Unsubscribe -->
+  <send retrans="500">
+    <![CDATA[
+      SUBSCRIBE sip:[service]@[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=[call_number]
+      To: "alice" <sip:[service]@[remote_ip]:[remote_port]>[$to_tag]
+      Call-ID: [call_id]
+      CSeq: 3 SUBSCRIBE
+      Contact: "alice" <sip:alice@[local_ip]:[local_port]>
+      Max-Forwards: 70
+      Event: message-summary
+      Expires: 0
+      Accept: application/simple-message-summary
+      Allow-Events: message-summary
+      User-Agent: SIPp
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200" />
+
+  <!-- Receive NOTIFY due to unsubscribe -->
+  <recv request="NOTIFY">
+    <action>
+      <ereg regexp="message-summary" check_it="true" search_in="hdr" header="Event" assign_to="event" />
+      <ereg regexp="terminated" check_it="true" search_in="hdr" header="Subscription-State" assign_to="state" />
+      <ereg regexp="Voice-Message: 1\/1" check_it="true" search_in="body" assign_to="vm" />
+    </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="event,state,vm" />
+
+</scenario>
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/aggregate/sipp/unsolicited.xml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/aggregate/sipp/unsolicited.xml
new file mode 100644
index 0000000..f3479d1
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/aggregate/sipp/unsolicited.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<scenario name="Unsolicited">
+
+  <recv request="NOTIFY">
+    <action>
+	  <ereg regexp="message-summary" check_it="true" search_in="hdr" header="Event" assign_to="event" />
+      <ereg regexp="terminated" check_it="true" search_in="hdr" header="Subscription-State" assign_to="state" />
+      <ereg regexp="Voice-Message: [3]\/[3]" check_it="true" search_in="body" assign_to="vm" />
+	  <!-- Get the call number and see if we've either met or acceded the expected number. -->
+      <assignstr assign_to="call_num" value="[call_number]" />
+      <todouble assign_to="count" variable="call_num" />
+      <test assign_to="expected" variable="count" compare="equal" value="2" />
+      <test assign_to="greater" variable="count" compare="greater_than" value="2" />
+    </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>
+
+  <!-- Fail the scenario if we've received more than the expected number of calls -->
+  <nop condexec="greater">
+    <action>
+      <error message="Received too many out of call NOTIFYs ([call_id])" />
+    </action>
+  </nop>
+
+  <!-- Pause for a time less than the global timeout, but long enough where we feel
+       that we won't potentially receive any erroneous calls after the expected call
+       count has been met. If we stop too soon then the scenario could have successfully
+       ended, but another unexpected message may have potentially arrived, which in
+       that case we should have failed the test. -->
+  <pause milliseconds="11000" />
+
+  <!-- Stop the scenario gracefully if we've received the expected number of calls
+	   Note, do this stops the test in a non-failure mode. -->
+  <nop condexec="expected">
+    <action>
+      <log message="call count met stopping test" />
+      <exec int_cmd="stop_gracefully" />
+    </action>
+  </nop>
+
+  <Reference variables="event,state,vm" />
+
+</scenario>
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/aggregate/test-config.yaml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/aggregate/test-config.yaml
new file mode 100644
index 0000000..29e2b82
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/aggregate/test-config.yaml
@@ -0,0 +1,74 @@
+testinfo:
+    summary: 'Test replacing an aggregated unsolicited subscription with a solicited one.'
+    description: |
+        'After enabling the mwi_subscribe_replaces_unsolicited option on an
+        endpoint, this tests to make sure an aggregated solicited subscription
+        for MWI is allowed to replace the unsolicited subscription, and
+        subsequently replaced by an unsolicited again when unsubscribed.
+
+        Note, this tests disables initial unsolicited at startup in order to
+        make the test a bit more deterministic.'
+
+properties:
+    dependencies:
+        - python: 'twisted'
+        - python: 'starpy'
+        - asterisk: 'res_pjsip'
+        - asterisk: 'res_pjsip_mwi'
+        - asterisk: 'res_mwi_external'
+        - asterisk: 'res_mwi_external_ami'
+        - sipp:
+            version: 'v3.0'
+    tags:
+        - pjsip
+
+test-modules:
+    test-object:
+        config-section: 'sipp-config'
+        typename: 'sipp.SIPpTestCase'
+    modules:
+        -
+            config-section: 'event-action-config'
+            typename: 'pluggable_modules.EventActionModule'
+
+sipp-config:
+    test-iterations:
+        -
+            # Due to the nature of SIPp, and it's handling of out of call messages
+            # we'll execute two scenarios. One to receive unsolicited MWI, and
+            # another to handle solicited MWI.
+            scenarios:
+                # For this test we expect to receive 2 unsolicited MWI messages.
+                # The first happens after the initial REGISTER (on contact
+                # updated). One NOTIFY is sent containing the combined message
+                # counts for "mb1" and "mb2". Upon unsubscribing, the replaced
+                # NOTIFY unsolicited subscription get recreated and sends out another
+                # again with the combined message count.
+                #
+                # Even though we expect only 2 unsolicited messages set the call count
+                # to 3 for the scenario. Once the expected call count is met the scenario
+                # is responsible for gracefully shutting itself down. See the scenario
+                # itself for more information.
+                #
+                # Note, we must tell SIPp to cause an error on scenario timeout.
+                # This way the test will fail if the expected number of calls is not met.
+                - { 'key-args': {'scenario': 'unsolicited.xml', '-i': '127.0.0.1', '-p': '5062', '-m': '3', '-timeout': '25', '-timeout_error': ''} }
+                - { 'key-args': {'scenario': 'solicited.xml', '-i': '127.0.0.1', '-p': '5061', '-s': 'alice'} }
+
+event-action-config:
+    -
+        ami-start:
+        ami-actions:
+            action:
+                Action: 'MWIUpdate'
+                Mailbox: 'mb1'
+                NewMessages: 1
+                OldMessages: 1
+    -
+        ami-start:
+        ami-actions:
+            action:
+                Action: 'MWIUpdate'
+                Mailbox: 'mb2'
+                NewMessages: 2
+                OldMessages: 2
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/multiple_registers/configs/ast1/pjsip.conf b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/multiple_registers/configs/ast1/pjsip.conf
new file mode 100644
index 0000000..65302f4
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/multiple_registers/configs/ast1/pjsip.conf
@@ -0,0 +1,29 @@
+[global]
+debug=yes
+mwi_disable_initial_unsolicited=yes
+
+[transport-template](!)
+type=transport
+bind=127.0.0.1:5060
+
+[transport-udp](transport-template)
+protocol=udp
+
+[aor-template](!)
+type=aor
+max_contacts=1
+
+[endpoint-template](!)
+type=endpoint
+context=default
+allow=!all,ulaw
+direct_media=false
+
+[alice](aor-template)
+mailboxes=mb1
+
+[alice](endpoint-template)
+aors=alice
+mailboxes=mb1
+mwi_subscribe_replaces_unsolicited=yes
+aggregate_mwi=no
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/multiple_registers/sipp/solicited.xml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/multiple_registers/sipp/solicited.xml
new file mode 100644
index 0000000..f9a6156
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/multiple_registers/sipp/solicited.xml
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<scenario name="Solicited">
+
+  <!-- Wait a few seconds before initiating the scenario to make sure
+	   the mailbox(es) have been updated -->
+  <pause milliseconds="3000" />
+
+  <!-- Once the contact is registered a SIP NOTIFY (for unsolicited MWI) is sent to
+	   the contact. Note, this registers the contact to port 5062 where SIP NOTIFY's
+	   for unsolicited MWI are expected -->
+  <send retrans="500">
+    <![CDATA[
+
+      REGISTER sip:[remote_ip]:[remote_port] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+      From: "[service]" <sip:[service]@[local_ip]:[local_port]>;tag=[call_number]
+      To: "[service]" <sip:[service]@[remote_ip]:[remote_port]>
+      Call-ID: [call_id]
+      CSeq: [cseq] REGISTER
+      Max-Forwards: 70
+      Contact: <sip:[service]@[local_ip]:5062;transport=[transport]>
+      Expires: 60
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200" />
+
+  <!-- Wait a few more seconds to make sure the unsolicited NOTIFY is received
+	   before subscribing -->
+  <pause milliseconds="3000" />
+
+  <!-- Now subscribe -->
+  <send retrans="500">
+    <![CDATA[
+      SUBSCRIBE sip:[service]@[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=[call_number]
+      To: "alice" <sip:[service]@[remote_ip]:[remote_port]>
+      Call-ID: [call_id]
+      CSeq: 1 SUBSCRIBE
+      Contact: "alice" <sip:alice@[local_ip]:[local_port]>
+      Max-Forwards: 70
+      Event: message-summary
+      Expires: 60
+      Accept: application/simple-message-summary
+      Allow-Events: message-summary
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200" />
+
+  <!-- Receive NOTIFY due to SUBSCRIBE -->
+  <recv request="NOTIFY">
+    <action>
+      <ereg regexp="message-summary" check_it="true" search_in="hdr" header="Event" assign_to="event" />
+      <ereg regexp="active;expires=[5,6][0-9]" check_it="true" search_in="hdr" header="Subscription-State" assign_to="state" />
+      <ereg regexp="Voice-Message: 1\/1" check_it="true" search_in="body" assign_to="vm" />
+    </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>
+
+  <pause milliseconds="1000" />
+
+  <!-- Now send multiple REGISTERs "updating the contact" -->
+  <send retrans="500">
+    <![CDATA[
+
+      REGISTER sip:[remote_ip]:[remote_port] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+      From: "[service]" <sip:[service]@[local_ip]:[local_port]>;tag=[call_number]
+      To: "[service]" <sip:[service]@[remote_ip]:[remote_port]>
+      Call-ID: [call_id]
+      CSeq: [cseq] REGISTER
+      Max-Forwards: 70
+      Contact: <sip:[service]@[local_ip]:5062;transport=[transport]>
+      Expires: 180
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200" />
+
+  <pause milliseconds="1000" />
+
+  <send retrans="500">
+    <![CDATA[
+
+      REGISTER sip:[remote_ip]:[remote_port] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+      From: "[service]" <sip:[service]@[local_ip]:[local_port]>;tag=[call_number]
+      To: "[service]" <sip:[service]@[remote_ip]:[remote_port]>
+      Call-ID: [call_id]
+      CSeq: [cseq] REGISTER
+      Max-Forwards: 70
+      Contact: <sip:[service]@[local_ip]:5062;transport=[transport]>
+      Expires: 120
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200" />
+
+  <pause milliseconds="1000" />
+
+  <send retrans="500">
+    <![CDATA[
+
+      REGISTER sip:[remote_ip]:[remote_port] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+      From: "[service]" <sip:[service]@[local_ip]:[local_port]>;tag=[call_number]
+      To: "[service]" <sip:[service]@[remote_ip]:[remote_port]>
+      Call-ID: [call_id]
+      CSeq: [cseq] REGISTER
+      Max-Forwards: 70
+      Contact: <sip:[service]@[local_ip]:5062;transport=[transport]>
+      Expires: 60
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200" />
+
+  <Reference variables="event,state,vm" />
+
+</scenario>
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/multiple_registers/sipp/unsolicited.xml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/multiple_registers/sipp/unsolicited.xml
new file mode 100644
index 0000000..4b71490
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/multiple_registers/sipp/unsolicited.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<scenario name="Unsolicited">
+
+  <recv request="NOTIFY">
+    <action>
+	  <ereg regexp="message-summary" check_it="true" search_in="hdr" header="Event" assign_to="event" />
+      <ereg regexp="terminated" check_it="true" search_in="hdr" header="Subscription-State" assign_to="state" />
+      <ereg regexp="Voice-Message: 1\/1" check_it="true" search_in="body" assign_to="vm" />
+	  <!-- Get the call number and see if we've either met or acceded the expected number. -->
+      <assignstr assign_to="call_num" value="[call_number]" />
+      <todouble assign_to="count" variable="call_num" />
+      <test assign_to="expected" variable="count" compare="equal" value="1" />
+      <test assign_to="greater" variable="count" compare="greater_than" value="1" />
+    </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>
+
+  <!-- Fail the scenario if we've received more than the expected number of calls -->
+  <nop condexec="greater">
+    <action>
+      <error message="Received too many out of call NOTIFYs ([call_id])" />
+    </action>
+  </nop>
+
+  <!-- Pause for a time less than the global timeout, but long enough where we feel
+       that we won't potentially receive any erroneous calls after the expected call
+       count has been met. If we stop too soon then the scenario could have successfully
+       ended, but another unexpected message may have potentially arrived, which in
+       that case we should have failed the test. -->
+  <pause milliseconds="12000" />
+
+  <!-- Stop the scenario gracefully if we've received the expected number of calls
+	   Note, do this stops the test in a non-failure mode. -->
+  <nop condexec="expected">
+    <action>
+      <log message="call count met stopping test" />
+      <exec int_cmd="stop_gracefully" />
+    </action>
+  </nop>
+
+  <Reference variables="event,state,vm" />
+
+</scenario>
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/multiple_registers/test-config.yaml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/multiple_registers/test-config.yaml
new file mode 100644
index 0000000..2de0b07
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/multiple_registers/test-config.yaml
@@ -0,0 +1,70 @@
+testinfo:
+    summary: 'Test replacing an unsolicited subscription and multiple REGISTERs.'
+    description: |
+        'After enabling the mwi_subscribe_replaces_unsolicited option on an
+        endpoint, this tests to make sure a solicited subscription for MWI is
+        allowed to replace the unsolicited subscription. It also then verifies
+        that after subscribing, NOTIFYs for unsolicited subscriptions are not
+        received for a given mailbox upon the contact being updated.
+
+        Note, this tests disables initial unsolicited at startup in order to
+        make the test a bit more deterministic.'
+
+properties:
+    dependencies:
+        - python: 'twisted'
+        - python: 'starpy'
+        - asterisk: 'res_pjsip'
+        - asterisk: 'res_pjsip_mwi'
+        - asterisk: 'res_mwi_external'
+        - asterisk: 'res_mwi_external_ami'
+        - sipp:
+            version: 'v3.0'
+    tags:
+        - pjsip
+
+test-modules:
+    test-object:
+        config-section: 'sipp-config'
+        typename: 'sipp.SIPpTestCase'
+    modules:
+        -
+            config-section: 'event-action-config'
+            typename: 'pluggable_modules.EventActionModule'
+
+sipp-config:
+    # The reactor-timeout needs to be lower than then unsolicited scenario timeout
+    # or there's a risk the test could "pass" even though the call count was not
+    # met for the scenario.
+    reactor-timeout: 20
+    test-iterations:
+        -
+            # Due to the nature of SIPp, and it's handling of out of call messages
+            # we'll execute two scenarios. One to receive unsolicited MWI, and
+            # another to handle solicited MWI.
+            scenarios:
+                # For this test we expect to receive 1 unsolicited MWI message.
+                # A NOTIFY is sent for unsolicited after the initial REGISTER
+                # (on contact updated). After that the solicited subscription
+                # replaces the unsolicited one so no more unsolicited MWI messages
+                # should be sent even if the contact is updated multiple times.
+                #
+                # Even though we expect only 1 unsolicited message set the call count
+                # to 2 for the scenario. Once the expected call count is met the scenario
+                # is responsible for gracefully shutting itself down. See the scenario
+                # itself for more information.
+                #
+                # Note, we must tell SIPp to cause an error on scenario timeout.
+                # This way the test will fail if the expected number of calls is not met.
+                - { 'key-args': {'scenario': 'unsolicited.xml', '-i': '127.0.0.1', '-p': '5062', '-m': '2', '-timeout': '25', '-timeout_error': ''} }
+                - { 'key-args': {'scenario': 'solicited.xml', '-i': '127.0.0.1', '-p': '5061', '-s': 'alice'} }
+
+event-action-config:
+    -
+        ami-start:
+        ami-actions:
+            action:
+                Action: 'MWIUpdate'
+                Mailbox: 'mb1'
+                NewMessages: 1
+                OldMessages: 1
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/non_aggregate/configs/ast1/pjsip.conf b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/non_aggregate/configs/ast1/pjsip.conf
new file mode 100644
index 0000000..e047cf3
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/non_aggregate/configs/ast1/pjsip.conf
@@ -0,0 +1,29 @@
+[global]
+debug=yes
+mwi_disable_initial_unsolicited=yes
+
+[transport-template](!)
+type=transport
+bind=127.0.0.1:5060
+
+[transport-udp](transport-template)
+protocol=udp
+
+[aor-template](!)
+type=aor
+max_contacts=1
+
+[endpoint-template](!)
+type=endpoint
+context=default
+allow=!all,ulaw
+direct_media=false
+
+[alice](aor-template)
+mailboxes=mb1
+
+[alice](endpoint-template)
+aors=alice
+mailboxes=mb1,mb2
+mwi_subscribe_replaces_unsolicited=yes
+aggregate_mwi=no
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/non_aggregate/sipp/solicited.xml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/non_aggregate/sipp/solicited.xml
new file mode 100644
index 0000000..cb0d96e
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/non_aggregate/sipp/solicited.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<scenario name="Solicited">
+
+  <!-- Wait a few seconds before initiating the scenario to make sure
+	   the mailbox(es) have been updated -->
+  <pause milliseconds="3000" />
+
+  <!-- Once the contact is registered a SIP NOTIFY (for unsolicited MWI) is sent to
+	   the contact. Note, this registers the contact to port 5062 where SIP NOTIFY's
+	   for unsolicited MWI are expected -->
+  <send retrans="500">
+    <![CDATA[
+
+      REGISTER sip:[remote_ip]:[remote_port] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+      From: "[service]" <sip:[service]@[local_ip]:[local_port]>;tag=[call_number]
+      To: "[service]" <sip:[service]@[remote_ip]:[remote_port]>
+      Call-ID: [call_id]
+      CSeq: [cseq] REGISTER
+      Max-Forwards: 70
+      Contact: <sip:[service]@[local_ip]:5062;transport=[transport]>
+      Expires: 60
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200" />
+
+  <!-- Wait a few more seconds to make sure the unsolicited NOTIFY is received
+	   before subscribing -->
+  <pause milliseconds="3000" />
+
+  <!-- Now subscribe -->
+  <send retrans="500">
+    <![CDATA[
+      SUBSCRIBE sip:[service]@[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=[call_number]
+      To: "alice" <sip:[service]@[remote_ip]:[remote_port]>
+      Call-ID: [call_id]
+      CSeq: 1 SUBSCRIBE
+      Contact: "alice" <sip:alice@[local_ip]:[local_port]>
+      Max-Forwards: 70
+      Event: message-summary
+      Expires: 60
+      Accept: application/simple-message-summary
+      Allow-Events: message-summary
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200">
+    <action>
+          <!-- Get the tag to use when unsubscribing -->
+          <ereg regexp="(;tag=.*)" header="To:" search_in="hdr" check_it="true" assign_to="to_tag"/>
+    </action>
+  </recv>
+
+  <!-- Receive NOTIFY due to SUBSCRIBE -->
+  <recv request="NOTIFY">
+    <action>
+      <ereg regexp="message-summary" check_it="true" search_in="hdr" header="Event" assign_to="event" />
+      <ereg regexp="active;expires=[5,6][0-9]" check_it="true" search_in="hdr" header="Subscription-State" assign_to="state" />
+      <ereg regexp="Voice-Message: 1\/1" check_it="true" search_in="body" assign_to="vm" />
+    </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>
+
+  <pause milliseconds="1000" />
+
+  <!-- Unsubscribe -->
+  <send retrans="500">
+    <![CDATA[
+      SUBSCRIBE sip:[service]@[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=[call_number]
+      To: "alice" <sip:[service]@[remote_ip]:[remote_port]>[$to_tag]
+      Call-ID: [call_id]
+      CSeq: 3 SUBSCRIBE
+      Contact: "alice" <sip:alice@[local_ip]:[local_port]>
+      Max-Forwards: 70
+      Event: message-summary
+      Expires: 0
+      Accept: application/simple-message-summary
+      Allow-Events: message-summary
+      User-Agent: SIPp
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200" />
+
+  <!-- Receive NOTIFY due to unsubscribe -->
+  <recv request="NOTIFY">
+    <action>
+      <ereg regexp="message-summary" check_it="true" search_in="hdr" header="Event" assign_to="event" />
+      <ereg regexp="terminated" check_it="true" search_in="hdr" header="Subscription-State" assign_to="state" />
+      <ereg regexp="Voice-Message: 1\/1" check_it="true" search_in="body" assign_to="vm" />
+    </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="event,state,vm" />
+
+</scenario>
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/non_aggregate/sipp/unsolicited.xml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/non_aggregate/sipp/unsolicited.xml
new file mode 100644
index 0000000..f1d0b94
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/non_aggregate/sipp/unsolicited.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<scenario name="Unsolicited">
+
+  <recv request="NOTIFY">
+    <action>
+	  <ereg regexp="message-summary" check_it="true" search_in="hdr" header="Event" assign_to="event" />
+      <ereg regexp="terminated" check_it="true" search_in="hdr" header="Subscription-State" assign_to="state" />
+      <ereg regexp="Voice-Message: [1|2]\/[1|2]" check_it="true" search_in="body" assign_to="vm" />
+	  <!-- Get the call number and see if we've either met or acceded the expected number. -->
+      <assignstr assign_to="call_num" value="[call_number]" />
+      <todouble assign_to="count" variable="call_num" />
+      <test assign_to="expected" variable="count" compare="equal" value="3" />
+      <test assign_to="greater" variable="count" compare="greater_than" value="3" />
+    </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>
+
+  <!-- Fail the scenario if we've received more than the expected number of calls -->
+  <nop condexec="greater">
+    <action>
+      <error message="Received too many out of call NOTIFYs ([call_id])" />
+    </action>
+  </nop>
+
+  <!-- Pause for a time less than the global timeout, but long enough where we feel
+       that we won't potentially receive any erroneous calls after the expected call
+       count has been met. If we stop too soon then the scenario could have successfully
+       ended, but another unexpected message may have potentially arrived, which in
+       that case we should have failed the test. -->
+  <pause milliseconds="11000" />
+
+  <!-- Stop the scenario gracefully if we've received the expected number of calls
+	   Note, do this stops the test in a non-failure mode. -->
+  <nop condexec="expected">
+    <action>
+      <log message="call count met stopping test" />
+      <exec int_cmd="stop_gracefully" />
+    </action>
+  </nop>
+
+  <Reference variables="event,state,vm" />
+
+</scenario>
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/non_aggregate/test-config.yaml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/non_aggregate/test-config.yaml
new file mode 100644
index 0000000..54dc4ae
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/non_aggregate/test-config.yaml
@@ -0,0 +1,74 @@
+testinfo:
+    summary: 'Test replacing an unsolicited subscription with a solicited one.'
+    description: |
+        'After enabling the mwi_subscribe_replaces_unsolicited option on an
+        endpoint, this tests to make sure a solicited subscription for MWI is
+        allowed to replace the unsolicited subscription, and subsequently
+        replaced by an unsolicited again when unsubscribed.
+
+        Note, this tests disables initial unsolicited at startup in order to
+        make the test a bit more deterministic.'
+
+properties:
+    dependencies:
+        - python: 'twisted'
+        - python: 'starpy'
+        - asterisk: 'res_pjsip'
+        - asterisk: 'res_pjsip_mwi'
+        - asterisk: 'res_mwi_external'
+        - asterisk: 'res_mwi_external_ami'
+        - sipp:
+            version: 'v3.0'
+    tags:
+        - pjsip
+
+test-modules:
+    test-object:
+        config-section: 'sipp-config'
+        typename: 'sipp.SIPpTestCase'
+    modules:
+        -
+            config-section: 'event-action-config'
+            typename: 'pluggable_modules.EventActionModule'
+
+sipp-config:
+    test-iterations:
+        -
+            # Due to the nature of SIPp, and it's handling of out of call messages
+            # we'll execute two scenarios. One to receive unsolicited MWI, and
+            # another to handle solicited MWI.
+            scenarios:
+                # For this test we expect to receive 3 unsolicited MWI messages.
+                # The first two happen after the initial REGISTER (on contact
+                # updated). One for "mb1" and one for "mb2". The third, and last
+                # one occurs after the solicited scenario unsubscribes. Upon
+                # unsubscribing, the replaced unsolicited subscription get recreated
+                # and sends out another NOTIFY
+                #
+                # Even though we expect only 3 unsolicited messages set the call count
+                # to 4 for the scenario. Once the expected call count is met the scenario
+                # is responsible for gracefully shutting itself down. See the scenario
+                # itself for more information.
+                #
+                # Note, we must tell SIPp to cause an error on scenario timeout.
+                # This way the test will fail if the expected number of calls is not met.
+                - { 'key-args': {'scenario': 'unsolicited.xml', '-i': '127.0.0.1', '-p': '5062', '-m': '4', '-timeout': '25', '-timeout_error': ''} }
+                - { 'key-args': {'scenario': 'solicited.xml', '-i': '127.0.0.1', '-p': '5061', '-s': 'alice'} }
+
+event-action-config:
+    -
+        ami-start:
+        ami-actions:
+            action:
+                Action: 'MWIUpdate'
+                Mailbox: 'mb1'
+                NewMessages: 1
+                OldMessages: 1
+    -
+        ami-start:
+        ami-actions:
+            action:
+                Action: 'MWIUpdate'
+                Mailbox: 'mb2'
+                NewMessages: 2
+                OldMessages: 2
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/tests.yaml b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/tests.yaml
new file mode 100644
index 0000000..c89e193
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/replaces_unsolicited/tests.yaml
@@ -0,0 +1,5 @@
+# Enter tests here in the order they should be considered for execution:
+tests:
+    - test: 'aggregate'
+    - test: 'non_aggregate'
+    - test: 'multiple_registers'
diff --git a/tests/channels/pjsip/subscriptions/mwi/solicited/tests.yaml b/tests/channels/pjsip/subscriptions/mwi/solicited/tests.yaml
new file mode 100644
index 0000000..ecad5b6
--- /dev/null
+++ b/tests/channels/pjsip/subscriptions/mwi/solicited/tests.yaml
@@ -0,0 +1,4 @@
+# Enter tests here in the order they should be considered for execution:
+tests:
+    - dir: 'replaces_unsolicited'
+    - dir: 'replaced_by_unsolicited'
diff --git a/tests/channels/pjsip/subscriptions/mwi/tests.yaml b/tests/channels/pjsip/subscriptions/mwi/tests.yaml
index 70b24dd..3636508 100644
--- a/tests/channels/pjsip/subscriptions/mwi/tests.yaml
+++ b/tests/channels/pjsip/subscriptions/mwi/tests.yaml
@@ -1,6 +1,8 @@
 # Enter tests here in the order they should be considered for execution:
 tests:
+    - dir: 'solicited'
     - dir: 'unsolicited'
+    - test: 'conflict'
     - test: 'missing_aor'
     - test: 'mwi_aggregate'
     - test: 'mwi_devstate'

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

Gerrit-Project: testsuite
Gerrit-Branch: 17
Gerrit-Change-Id: I50ebd212789249f14056b8b3948304237a5f1042
Gerrit-Change-Number: 12821
Gerrit-PatchSet: 2
Gerrit-Owner: Kevin Harwell <kharwell at digium.com>
Gerrit-Reviewer: Friendly Automation
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20190910/211e7186/attachment-0001.html>


More information about the asterisk-code-review mailing list