<p>Friendly Automation <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/testsuite/+/14210">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Joshua Colp: Looks good to me, but someone else must approve
Kevin Harwell: Looks good to me, but someone else must approve
George Joseph: Looks good to me, approved
Friendly Automation: Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">app_queue: check for leaking Stasis subscriptions after Redirect()<br><br>ASTERISK-28829<br><br>Change-Id: I90451cca574e2fa7d825b4cdc0bab03e331c0e9b<br>---<br>A lib/python/asterisk/taskprocessor_test_condition.py<br>M test-config.yaml<br>A tests/apps/queues/redirect/configs/ast1/extensions.conf<br>A tests/apps/queues/redirect/configs/ast1/pjsip.conf<br>A tests/apps/queues/redirect/configs/ast1/queues.conf<br>A tests/apps/queues/redirect/test-config.yaml<br>M tests/apps/queues/tests.yaml<br>7 files changed, 371 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/lib/python/asterisk/taskprocessor_test_condition.py b/lib/python/asterisk/taskprocessor_test_condition.py</span><br><span>new file mode 100644</span><br><span>index 0000000..889b69b</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/asterisk/taskprocessor_test_condition.py</span><br><span>@@ -0,0 +1,151 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#!/usr/bin/env python</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+import logging</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from twisted.internet import defer</span><br><span style="color: hsl(120, 100%, 40%);">+from .test_conditions import TestCondition</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+LOGGER = logging.getLogger(__name__)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# a list of task processors that should be ignored by this test</span><br><span style="color: hsl(120, 100%, 40%);">+IGNORED_TASKPROCESSORS = [</span><br><span style="color: hsl(120, 100%, 40%);">+ # the outsess traskprocessor will exist until 30 seconds after the call has ended</span><br><span style="color: hsl(120, 100%, 40%);">+ 'pjsip/outsess/'</span><br><span style="color: hsl(120, 100%, 40%);">+]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class Taskprocessor(object):</span><br><span style="color: hsl(120, 100%, 40%);">+ """A small object that tracks a task processor.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ The object parses a line from the Asterisk CLI command core show taskprocessors to</span><br><span style="color: hsl(120, 100%, 40%);">+ populate its values</span><br><span style="color: hsl(120, 100%, 40%);">+ """</span><br><span style="color: hsl(120, 100%, 40%);">+ processor = ""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def __init__(self, line):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Constructor</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ Keyword Arguments:</span><br><span style="color: hsl(120, 100%, 40%);">+ line A raw line received from Asterisk that describes a task processor</span><br><span style="color: hsl(120, 100%, 40%);">+ """</span><br><span style="color: hsl(120, 100%, 40%);">+ line = line.strip()</span><br><span style="color: hsl(120, 100%, 40%);">+ tokens = line.split()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if len(tokens) == 6:</span><br><span style="color: hsl(120, 100%, 40%);">+ self.processor = tokens[0].strip()</span><br><span style="color: hsl(120, 100%, 40%);">+ self.processed = int(tokens[1])</span><br><span style="color: hsl(120, 100%, 40%);">+ self.in_queue = int(tokens[2])</span><br><span style="color: hsl(120, 100%, 40%);">+ self.max_depth = int(tokens[3])</span><br><span style="color: hsl(120, 100%, 40%);">+ self.low_water = int(tokens[4])</span><br><span style="color: hsl(120, 100%, 40%);">+ self.high_water = int(tokens[5])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class TaskprocessorTestCondition(TestCondition):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Base class for the task processor pre- and post-test</span><br><span style="color: hsl(120, 100%, 40%);">+ checks. This class provides a common mechanism to get the task</span><br><span style="color: hsl(120, 100%, 40%);">+ processors from Asterisk and populate them in a dictionary for</span><br><span style="color: hsl(120, 100%, 40%);">+ tracking</span><br><span style="color: hsl(120, 100%, 40%);">+ """</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def __init__(self, test_config):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Constructor</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ Keyword Arguments:</span><br><span style="color: hsl(120, 100%, 40%);">+ test_config The test config object</span><br><span style="color: hsl(120, 100%, 40%);">+ """</span><br><span style="color: hsl(120, 100%, 40%);">+ super(TaskprocessorTestCondition, self).__init__(test_config)</span><br><span style="color: hsl(120, 100%, 40%);">+ self.task_processors = {}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def is_taskprocessor_ignored(self, name):</span><br><span style="color: hsl(120, 100%, 40%);">+ for ignored_taskprocessor in IGNORED_TASKPROCESSORS:</span><br><span style="color: hsl(120, 100%, 40%);">+ if name.startswith(ignored_taskprocessor):</span><br><span style="color: hsl(120, 100%, 40%);">+ return True</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return False</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def get_task_processors(self, ast):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Get the task processors from some instance of Asterisk"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def __show_taskprocessors_callback(result):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Callback when CLI command has finished"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ lines = result.output</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'No such command' in lines:</span><br><span style="color: hsl(120, 100%, 40%);">+ return result</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'Unable to connect to remote asterisk' in lines:</span><br><span style="color: hsl(120, 100%, 40%);">+ return result</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ line_tokens = lines.split('\n')</span><br><span style="color: hsl(120, 100%, 40%);">+ task_processors = []</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for line in line_tokens:</span><br><span style="color: hsl(120, 100%, 40%);">+ task_processor = Taskprocessor(line)</span><br><span style="color: hsl(120, 100%, 40%);">+ if task_processor.processor != '' and not self.is_taskprocessor_ignored(task_processor.processor):</span><br><span style="color: hsl(120, 100%, 40%);">+ LOGGER.debug("Tracking %s", task_processor.processor)</span><br><span style="color: hsl(120, 100%, 40%);">+ task_processors.append(task_processor)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ self.task_processors[result.host] = task_processors</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast.cli_exec("core show taskprocessors").addCallback(__show_taskprocessors_callback)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class TaskprocessorPreTestCondition(TaskprocessorTestCondition):</span><br><span style="color: hsl(120, 100%, 40%);">+ """The Task Processor Pre-TestCondition object"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def evaluate(self, related_test_condition=None):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Evaluate the test condition"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def __raise_finished(result, finish_deferred):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Called when all CLI commands have finished"""</span><br><span style="color: hsl(120, 100%, 40%);">+ finished_deferred.callback(self)</span><br><span style="color: hsl(120, 100%, 40%);">+ return result</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Automatically pass the pre-test condition - whatever task processors</span><br><span style="color: hsl(120, 100%, 40%);">+ # are currently open are needed by Asterisk and merely expected to exist</span><br><span style="color: hsl(120, 100%, 40%);">+ # when the test is finished</span><br><span style="color: hsl(120, 100%, 40%);">+ super(TaskprocessorPreTestCondition, self).pass_check()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ finished_deferred = defer.Deferred()</span><br><span style="color: hsl(120, 100%, 40%);">+ exec_list = [super(TaskprocessorPreTestCondition, self).get_task_processors(ast)</span><br><span style="color: hsl(120, 100%, 40%);">+ for ast in self.ast]</span><br><span style="color: hsl(120, 100%, 40%);">+ defer.DeferredList(exec_list).addCallback(__raise_finished, finished_deferred)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return finished_deferred</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class TaskprocessorPostTestCondition(TaskprocessorTestCondition):</span><br><span style="color: hsl(120, 100%, 40%);">+ """The Task Processor Post-TestCondition object"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def evaluate(self, related_test_condition=None):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Evaluate the test condition"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ def __task_processors_obtained(result, finished_deferred):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Callback when all CLI commands have finished"""</span><br><span style="color: hsl(120, 100%, 40%);">+ for ast_host in related_test_condition.task_processors.keys():</span><br><span style="color: hsl(120, 100%, 40%);">+ if not ast_host in self.task_processors:</span><br><span style="color: hsl(120, 100%, 40%);">+ super(TaskprocessorPostTestCondition, self).fail_check(</span><br><span style="color: hsl(120, 100%, 40%);">+ "Asterisk host in pre-test check [%s]"</span><br><span style="color: hsl(120, 100%, 40%);">+ " not found in post-test check" % ast_host)</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ # Find all task processors in post-check not in pre-check</span><br><span style="color: hsl(120, 100%, 40%);">+ for task_processor in self.task_processors[ast_host]:</span><br><span style="color: hsl(120, 100%, 40%);">+ if (len([</span><br><span style="color: hsl(120, 100%, 40%);">+ t for t</span><br><span style="color: hsl(120, 100%, 40%);">+ in related_test_condition.task_processors[ast_host]</span><br><span style="color: hsl(120, 100%, 40%);">+ if task_processor.processor == t.processor]) == 0):</span><br><span style="color: hsl(120, 100%, 40%);">+ super(TaskprocessorPostTestCondition, self).fail_check(</span><br><span style="color: hsl(120, 100%, 40%);">+ "Failed to find task processor %s in "</span><br><span style="color: hsl(120, 100%, 40%);">+ "pre-test check" % (task_processor.processor))</span><br><span style="color: hsl(120, 100%, 40%);">+ super(TaskprocessorPostTestCondition, self).pass_check()</span><br><span style="color: hsl(120, 100%, 40%);">+ finished_deferred.callback(self)</span><br><span style="color: hsl(120, 100%, 40%);">+ return result</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if related_test_condition is None:</span><br><span style="color: hsl(120, 100%, 40%);">+ msg = "No pre-test condition object provided"</span><br><span style="color: hsl(120, 100%, 40%);">+ super(TaskprocessorPostTestCondition, self).fail_check(msg)</span><br><span style="color: hsl(120, 100%, 40%);">+ return</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ finished_deferred = defer.Deferred()</span><br><span style="color: hsl(120, 100%, 40%);">+ exec_list = [super(TaskprocessorPostTestCondition, self).get_task_processors(ast)</span><br><span style="color: hsl(120, 100%, 40%);">+ for ast in self.ast]</span><br><span style="color: hsl(120, 100%, 40%);">+ defer.DeferredList(exec_list).addCallback(__task_processors_obtained, finished_deferred)</span><br><span style="color: hsl(120, 100%, 40%);">+ return finished_deferred</span><br><span>diff --git a/test-config.yaml b/test-config.yaml</span><br><span>index 6ea1339..dd00437 100644</span><br><span>--- a/test-config.yaml</span><br><span>+++ b/test-config.yaml</span><br><span>@@ -64,6 +64,13 @@</span><br><span> post:</span><br><span> typename: 'memory_test_condition.MemoryPostTestCondition'</span><br><span> related-type: 'memory_test_condition.MemoryPreTestCondition'</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ name: 'taskprocessors'</span><br><span style="color: hsl(120, 100%, 40%);">+ pre:</span><br><span style="color: hsl(120, 100%, 40%);">+ typename: 'taskprocessor_test_condition.TaskprocessorPreTestCondition'</span><br><span style="color: hsl(120, 100%, 40%);">+ post:</span><br><span style="color: hsl(120, 100%, 40%);">+ typename: 'taskprocessor_test_condition.TaskprocessorPostTestCondition'</span><br><span style="color: hsl(120, 100%, 40%);">+ related-type: 'taskprocessor_test_condition.TaskprocessorPreTestCondition'</span><br><span> </span><br><span> # Exclude all long-running tests (greater than one minute)</span><br><span> config-fast:</span><br><span>diff --git a/tests/apps/queues/redirect/configs/ast1/extensions.conf b/tests/apps/queues/redirect/configs/ast1/extensions.conf</span><br><span>new file mode 100644</span><br><span>index 0000000..ae535bf</span><br><span>--- /dev/null</span><br><span>+++ b/tests/apps/queues/redirect/configs/ast1/extensions.conf</span><br><span>@@ -0,0 +1,13 @@</span><br><span style="color: hsl(120, 100%, 40%);">+[default]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+exten => 101,1,Queue(queue,Rtc)</span><br><span style="color: hsl(120, 100%, 40%);">+exten => 102,1,Wait(5)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+[park]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+exten => s,1,Wait(5)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+[dialbob]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+exten => s,1,Dial(PJSIP/bob)</span><br><span style="color: hsl(120, 100%, 40%);">+ same => Hangup()</span><br><span>diff --git a/tests/apps/queues/redirect/configs/ast1/pjsip.conf b/tests/apps/queues/redirect/configs/ast1/pjsip.conf</span><br><span>new file mode 100644</span><br><span>index 0000000..c4add2f</span><br><span>--- /dev/null</span><br><span>+++ b/tests/apps/queues/redirect/configs/ast1/pjsip.conf</span><br><span>@@ -0,0 +1,33 @@</span><br><span style="color: hsl(120, 100%, 40%);">+[global]</span><br><span style="color: hsl(120, 100%, 40%);">+type=global</span><br><span style="color: hsl(120, 100%, 40%);">+debug=yes</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+[local]</span><br><span style="color: hsl(120, 100%, 40%);">+type=transport</span><br><span style="color: hsl(120, 100%, 40%);">+protocol=udp</span><br><span style="color: hsl(120, 100%, 40%);">+bind=127.0.0.1:5060</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+[endpoint_t](!)</span><br><span style="color: hsl(120, 100%, 40%);">+type=endpoint</span><br><span style="color: hsl(120, 100%, 40%);">+context=default</span><br><span style="color: hsl(120, 100%, 40%);">+direct_media=no</span><br><span style="color: hsl(120, 100%, 40%);">+disallow=all</span><br><span style="color: hsl(120, 100%, 40%);">+allow=ulaw</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+[aor_t](!)</span><br><span style="color: hsl(120, 100%, 40%);">+type=aor</span><br><span style="color: hsl(120, 100%, 40%);">+max_contacts=1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+[alice](aor_t)</span><br><span style="color: hsl(120, 100%, 40%);">+[alice](endpoint_t)</span><br><span style="color: hsl(120, 100%, 40%);">+aors=alice</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+[bob](aor_t)</span><br><span style="color: hsl(120, 100%, 40%);">+[bob](endpoint_t)</span><br><span style="color: hsl(120, 100%, 40%);">+aors=bob</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+[carol](aor_t)</span><br><span style="color: hsl(120, 100%, 40%);">+[carol](endpoint_t)</span><br><span style="color: hsl(120, 100%, 40%);">+aors=carol</span><br><span>diff --git a/tests/apps/queues/redirect/configs/ast1/queues.conf b/tests/apps/queues/redirect/configs/ast1/queues.conf</span><br><span>new file mode 100644</span><br><span>index 0000000..cac3974</span><br><span>--- /dev/null</span><br><span>+++ b/tests/apps/queues/redirect/configs/ast1/queues.conf</span><br><span>@@ -0,0 +1,4 @@</span><br><span style="color: hsl(120, 100%, 40%);">+[general]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+[queue]</span><br><span style="color: hsl(120, 100%, 40%);">+member => Local/s@dialbob/n</span><br><span>diff --git a/tests/apps/queues/redirect/test-config.yaml b/tests/apps/queues/redirect/test-config.yaml</span><br><span>new file mode 100644</span><br><span>index 0000000..510dcc2</span><br><span>--- /dev/null</span><br><span>+++ b/tests/apps/queues/redirect/test-config.yaml</span><br><span>@@ -0,0 +1,162 @@</span><br><span style="color: hsl(120, 100%, 40%);">+testinfo:</span><br><span style="color: hsl(120, 100%, 40%);">+ summary: "Redirected queue call"</span><br><span style="color: hsl(120, 100%, 40%);">+ description: |</span><br><span style="color: hsl(120, 100%, 40%);">+ "We have Alice call the queue, which has Local/dialbob as member. Bob will</span><br><span style="color: hsl(120, 100%, 40%);">+ be called through this local channel. Upon answering, we'll have Carol call</span><br><span style="color: hsl(120, 100%, 40%);">+ the dialplan and Wait() there for further instructions.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ After Carol's call is setup, we Redirect() Alice's call to the dialplan,</span><br><span style="color: hsl(120, 100%, 40%);">+ which will break her call/bridge with Bob. We then use Bridge() to bridge</span><br><span style="color: hsl(120, 100%, 40%);">+ Alice's call with Carol's call that was still waiting in the dialplan.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ After this scenario succeeded, there should be no leaked Stasis subscriptions,</span><br><span style="color: hsl(120, 100%, 40%);">+ which we check by comparing the list of taskprocessors at the start of the test</span><br><span style="color: hsl(120, 100%, 40%);">+ with the list at the end of the test. ASTERISK-28829 will cause two subscriptions/</span><br><span style="color: hsl(120, 100%, 40%);">+ task processors to leak: stasis/p:bridge:all and stasis/p:channel:all"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+test-modules:</span><br><span style="color: hsl(120, 100%, 40%);">+ test-object:</span><br><span style="color: hsl(120, 100%, 40%);">+ config-section: test-object-config</span><br><span style="color: hsl(120, 100%, 40%);">+ typename: 'test_case.TestCaseModule'</span><br><span style="color: hsl(120, 100%, 40%);">+ modules:</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ config-section: pjsua-config</span><br><span style="color: hsl(120, 100%, 40%);">+ typename: 'phones.PjsuaPhoneController'</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ config-section: pluggable-config</span><br><span style="color: hsl(120, 100%, 40%);">+ typename: 'pluggable_modules.EventActionModule'</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+test-object-config:</span><br><span style="color: hsl(120, 100%, 40%);">+ connect-ami: True</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+pjsua-config:</span><br><span style="color: hsl(120, 100%, 40%);">+ transports:</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ name: 'local-ipv4-1'</span><br><span style="color: hsl(120, 100%, 40%);">+ bind: '127.0.0.1'</span><br><span style="color: hsl(120, 100%, 40%);">+ bindport: '5061'</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ name: 'local-ipv4-2'</span><br><span style="color: hsl(120, 100%, 40%);">+ bind: '127.0.0.1'</span><br><span style="color: hsl(120, 100%, 40%);">+ bindport: '5062'</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ name: 'local-ipv4-3'</span><br><span style="color: hsl(120, 100%, 40%);">+ bind: '127.0.0.1'</span><br><span style="color: hsl(120, 100%, 40%);">+ bindport: '5063'</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ accounts:</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ name: 'alice'</span><br><span style="color: hsl(120, 100%, 40%);">+ username: 'alice'</span><br><span style="color: hsl(120, 100%, 40%);">+ domain: '127.0.0.1'</span><br><span style="color: hsl(120, 100%, 40%);">+ transport: 'local-ipv4-1'</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ name: 'bob'</span><br><span style="color: hsl(120, 100%, 40%);">+ username: 'bob'</span><br><span style="color: hsl(120, 100%, 40%);">+ domain: '127.0.0.1'</span><br><span style="color: hsl(120, 100%, 40%);">+ transport: 'local-ipv4-2'</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ name: 'carol'</span><br><span style="color: hsl(120, 100%, 40%);">+ username: 'carol'</span><br><span style="color: hsl(120, 100%, 40%);">+ domain: '127.0.0.1'</span><br><span style="color: hsl(120, 100%, 40%);">+ transport: 'local-ipv4-3'</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+pluggable-config:</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ ami-events:</span><br><span style="color: hsl(120, 100%, 40%);">+ conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+ match:</span><br><span style="color: hsl(120, 100%, 40%);">+ Event: 'UserEvent'</span><br><span style="color: hsl(120, 100%, 40%);">+ UserEvent: 'PJsuaPhonesReady'</span><br><span style="color: hsl(120, 100%, 40%);">+ count: 1</span><br><span style="color: hsl(120, 100%, 40%);">+ # alice dials the queue</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsua_phone:</span><br><span style="color: hsl(120, 100%, 40%);">+ action: 'call'</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsua_account: 'alice'</span><br><span style="color: hsl(120, 100%, 40%);">+ call_uri: 'sip:101@127.0.0.1'</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ # queue rings bob, bob answers, alice is bridged with bob</span><br><span style="color: hsl(120, 100%, 40%);">+ ami-events:</span><br><span style="color: hsl(120, 100%, 40%);">+ conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+ match:</span><br><span style="color: hsl(120, 100%, 40%);">+ Event: 'BridgeEnter'</span><br><span style="color: hsl(120, 100%, 40%);">+ Channel: 'PJSIP/alice-00000000'</span><br><span style="color: hsl(120, 100%, 40%);">+ Context: 'default'</span><br><span style="color: hsl(120, 100%, 40%);">+ count: 1</span><br><span style="color: hsl(120, 100%, 40%);">+ # then carol starts a call that ends in the dialplan Wait()</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsua_phone:</span><br><span style="color: hsl(120, 100%, 40%);">+ action: 'call'</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsua_account: 'carol'</span><br><span style="color: hsl(120, 100%, 40%);">+ call_uri: 'sip:102@127.0.0.1'</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ # when we detect carol's call..</span><br><span style="color: hsl(120, 100%, 40%);">+ ami-events:</span><br><span style="color: hsl(120, 100%, 40%);">+ conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+ match:</span><br><span style="color: hsl(120, 100%, 40%);">+ Event: 'Newexten'</span><br><span style="color: hsl(120, 100%, 40%);">+ Channel: 'PJSIP/carol-00000002'</span><br><span style="color: hsl(120, 100%, 40%);">+ Exten: '102'</span><br><span style="color: hsl(120, 100%, 40%);">+ count: 1</span><br><span style="color: hsl(120, 100%, 40%);">+ # .. we redirect alice to a park extension (this will halt her call with bob)</span><br><span style="color: hsl(120, 100%, 40%);">+ ami-actions:</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ action:</span><br><span style="color: hsl(120, 100%, 40%);">+ Action: 'Redirect'</span><br><span style="color: hsl(120, 100%, 40%);">+ Channel: 'PJSIP/alice-00000000'</span><br><span style="color: hsl(120, 100%, 40%);">+ Context: 'park'</span><br><span style="color: hsl(120, 100%, 40%);">+ Exten: 's'</span><br><span style="color: hsl(120, 100%, 40%);">+ Priority: '1'</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ # wait for alice to be parked..</span><br><span style="color: hsl(120, 100%, 40%);">+ ami-events:</span><br><span style="color: hsl(120, 100%, 40%);">+ conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+ match:</span><br><span style="color: hsl(120, 100%, 40%);">+ Event: 'Newexten'</span><br><span style="color: hsl(120, 100%, 40%);">+ Channel: 'PJSIP/alice-00000000'</span><br><span style="color: hsl(120, 100%, 40%);">+ Context: 'park'</span><br><span style="color: hsl(120, 100%, 40%);">+ count: 1</span><br><span style="color: hsl(120, 100%, 40%);">+ # .. then bridge alice with carol</span><br><span style="color: hsl(120, 100%, 40%);">+ ami-actions:</span><br><span style="color: hsl(120, 100%, 40%);">+ action:</span><br><span style="color: hsl(120, 100%, 40%);">+ Action: 'Bridge'</span><br><span style="color: hsl(120, 100%, 40%);">+ Channel1: 'PJSIP/alice-00000000'</span><br><span style="color: hsl(120, 100%, 40%);">+ Channel2: 'PJSIP/carol-00000002'</span><br><span style="color: hsl(120, 100%, 40%);">+ # once alice has entered the bridge with carol, we're done</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ ami-events:</span><br><span style="color: hsl(120, 100%, 40%);">+ conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+ match:</span><br><span style="color: hsl(120, 100%, 40%);">+ Event: 'BridgeEnter'</span><br><span style="color: hsl(120, 100%, 40%);">+ Channel: 'PJSIP/carol-00000002'</span><br><span style="color: hsl(120, 100%, 40%);">+ Context: 'default'</span><br><span style="color: hsl(120, 100%, 40%);">+ count: 1</span><br><span style="color: hsl(120, 100%, 40%);">+ ami-actions:</span><br><span style="color: hsl(120, 100%, 40%);">+ action:</span><br><span style="color: hsl(120, 100%, 40%);">+ Action: 'Hangup'</span><br><span style="color: hsl(120, 100%, 40%);">+ Channel: 'PJSIP/alice-00000000'</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ ami-events:</span><br><span style="color: hsl(120, 100%, 40%);">+ conditions:</span><br><span style="color: hsl(120, 100%, 40%);">+ match:</span><br><span style="color: hsl(120, 100%, 40%);">+ Event: 'Hangup'</span><br><span style="color: hsl(120, 100%, 40%);">+ Channel: 'PJSIP/carol-00000002'</span><br><span style="color: hsl(120, 100%, 40%);">+ Context: 'default'</span><br><span style="color: hsl(120, 100%, 40%);">+ count: 1</span><br><span style="color: hsl(120, 100%, 40%);">+ stop_test:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+properties:</span><br><span style="color: hsl(120, 100%, 40%);">+ minversion: '13.6.0'</span><br><span style="color: hsl(120, 100%, 40%);">+ dependencies:</span><br><span style="color: hsl(120, 100%, 40%);">+ - python : twisted</span><br><span style="color: hsl(120, 100%, 40%);">+ - python : starpy</span><br><span style="color: hsl(120, 100%, 40%);">+ - python : pjsua</span><br><span style="color: hsl(120, 100%, 40%);">+ - asterisk : res_pjsip</span><br><span style="color: hsl(120, 100%, 40%);">+ - asterisk : app_queue</span><br><span style="color: hsl(120, 100%, 40%);">+ tags:</span><br><span style="color: hsl(120, 100%, 40%);">+ - pjsip</span><br><span style="color: hsl(120, 100%, 40%);">+ - queues</span><br><span style="color: hsl(120, 100%, 40%);">+ - chan_local</span><br><span style="color: hsl(120, 100%, 40%);">+ testconditions:</span><br><span style="color: hsl(120, 100%, 40%);">+ -</span><br><span style="color: hsl(120, 100%, 40%);">+ # this will check for leaking stasis subscriptions/taskprocessors at the end of the testrun</span><br><span style="color: hsl(120, 100%, 40%);">+ name: 'taskprocessors'</span><br><span>diff --git a/tests/apps/queues/tests.yaml b/tests/apps/queues/tests.yaml</span><br><span>index be78da0..e1c0d0c 100644</span><br><span>--- a/tests/apps/queues/tests.yaml</span><br><span>+++ b/tests/apps/queues/tests.yaml</span><br><span>@@ -16,3 +16,4 @@</span><br><span> - test: 'wrapup_time_per_member'</span><br><span> - test: 'reason_pause_ami'</span><br><span> - test: 'queue_member_forward'</span><br><span style="color: hsl(120, 100%, 40%);">+ - test: 'redirect'</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/testsuite/+/14210">change 14210</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/testsuite/+/14210"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: testsuite </div>
<div style="display:none"> Gerrit-Branch: 16 </div>
<div style="display:none"> Gerrit-Change-Id: I90451cca574e2fa7d825b4cdc0bab03e331c0e9b </div>
<div style="display:none"> Gerrit-Change-Number: 14210 </div>
<div style="display:none"> Gerrit-PatchSet: 4 </div>
<div style="display:none"> Gerrit-Owner: lvl <digium@lvlconsultancy.nl> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@sangoma.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Richard Mudgett <rmudgett@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>