Repository: qpid-dispatch Updated Branches: refs/heads/master 855011938 -> bdd2aa389
DISPATCH-1170 - Fix for system tests execution on python3 only machines. This closes #414 Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/bdd2aa38 Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/bdd2aa38 Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/bdd2aa38 Branch: refs/heads/master Commit: bdd2aa389dc8d5fb035e203cf6270142daeb2604 Parents: 8550119 Author: Fernando Giorgetti <[email protected]> Authored: Mon Nov 5 11:16:50 2018 -0200 Committer: Ganesh Murthy <[email protected]> Committed: Mon Nov 5 09:29:57 2018 -0500 ---------------------------------------------------------------------- CMakeLists.txt | 4 +- tests/CMakeLists.txt | 8 +- tests/system_tests_authz_service_plugin.py.in | 208 ++++++++++++++ tests/system_tests_handle_failover.py.in | 309 +++++++++++++++++++++ 4 files changed, 523 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/bdd2aa38/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/CMakeLists.txt b/CMakeLists.txt index 9dc7180..bfe2c4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,10 +48,8 @@ include(FindPythonLibs) if (PYTHON_VERSION_MAJOR STREQUAL 3) set(PY_STRING "python3") - set(PY_UNIT2_STRING "unit2-3") elseif(PYTHON_VERSION_MAJOR STREQUAL 2) set(PY_STRING "python") - set(PY_UNIT2_STRING "unit2") endif() # Find python-unittest2 @@ -244,6 +242,8 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/run.py.in ${CMAKE_CURRENT_BINARY_DIR} configure_file(${CMAKE_CURRENT_SOURCE_DIR}/run.py.in ${CMAKE_CURRENT_BINARY_DIR}/tests/run.py) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tests/authservice.py.in ${CMAKE_CURRENT_SOURCE_DIR}/tests/authservice.py) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tests/failoverserver.py.in ${CMAKE_CURRENT_SOURCE_DIR}/tests/failoverserver.py) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tests/system_tests_authz_service_plugin.py.in ${CMAKE_CURRENT_SOURCE_DIR}/tests/system_tests_authz_service_plugin.py) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tests/system_tests_handle_failover.py.in ${CMAKE_CURRENT_SOURCE_DIR}/tests/system_tests_handle_failover.py) execute_process(COMMAND ${RUN} --sh OUTPUT_FILE config.sh) if (NOT UNITTEST2_MISSING) http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/bdd2aa38/tests/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fe99dec..1075c76 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -67,9 +67,9 @@ add_test(unit_tests_size_1 ${TEST_WRAP} -x unit_tests_size 1) add_test(unit_tests ${TEST_WRAP} -x unit_tests ${CMAKE_CURRENT_SOURCE_DIR}/threads4.conf) # Unit test python modules -add_test(router_engine_test ${TEST_WRAP} -x ${PY_UNIT2_STRING} -v router_engine_test) -add_test(management_test ${TEST_WRAP} -x ${PY_UNIT2_STRING} -v management) -add_test(router_policy_test ${TEST_WRAP} -x ${PY_UNIT2_STRING} -v router_policy_test) +add_test(router_engine_test ${TEST_WRAP} -x unit2 -v router_engine_test) +add_test(management_test ${TEST_WRAP} -x unit2 -v management) +add_test(router_policy_test ${TEST_WRAP} -x unit2 -v router_policy_test) if(USE_LIBWEBSOCKETS) set(SYSTEM_TESTS_HTTP system_tests_http) @@ -125,7 +125,7 @@ foreach(py_test_module system_tests_core_client ) - add_test(${py_test_module} ${TEST_WRAP} -x ${PY_UNIT2_STRING} -v ${py_test_module}) + add_test(${py_test_module} ${TEST_WRAP} -x unit2 -v ${py_test_module}) list(APPEND SYSTEM_TEST_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${py_test_module}.py) endforeach() http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/bdd2aa38/tests/system_tests_authz_service_plugin.py.in ---------------------------------------------------------------------- diff --git a/tests/system_tests_authz_service_plugin.py.in b/tests/system_tests_authz_service_plugin.py.in new file mode 100644 index 0000000..a214df9 --- /dev/null +++ b/tests/system_tests_authz_service_plugin.py.in @@ -0,0 +1,208 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from __future__ import unicode_literals +from __future__ import division +from __future__ import absolute_import +from __future__ import print_function + +import unittest2 as unittest +import os, json +from subprocess import PIPE, Popen, STDOUT +from system_test import TestCase, Qdrouterd, main_module, DIR, TIMEOUT, Process, SkipIfNeeded +from proton import Array, Data, Message, SASL, symbol, UNDESCRIBED +from proton.handlers import MessagingHandler +from proton.reactor import Container + + +class AuthServicePluginAuthzTest(TestCase): + @classmethod + def addUser(cls, user, password): + # Create a sasl database. + p = Popen(['saslpasswd2', '-c', '-p', '-f', 'users.sasldb', user], + stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True) + result = p.communicate(password) + assert p.returncode == 0, "saslpasswd2 exit status %s, output:\n%s" % (p.returncode, result) + + @classmethod + def createSaslFiles(cls): + cls.addUser('guest', 'guest') + cls.addUser('admin', 'admin') + # Create a SASL configuration file. + with open('tests-mech-SCRAM.conf', 'w') as sasl_conf: + sasl_conf.write(""" +mech_list: SCRAM-SHA-1 PLAIN +""") + with open('proton-server.conf', 'w') as sasl_conf: + sasl_conf.write(""" +pwcheck_method: auxprop +auxprop_plugin: sasldb +sasldb_path: users.sasldb +mech_list: SCRAM-SHA-1 PLAIN +""") + + + @classmethod + def setUpClass(cls): + """ + Tests the delegation of sasl auth to an external auth service. + """ + super(AuthServicePluginAuthzTest, cls).setUpClass() + + if not SASL.extended(): + return + + cls.createSaslFiles() + + cls.auth_service_port = cls.tester.get_port() + cls.tester.popen(['/usr/bin/env', '${PY_STRING}', os.path.join(os.path.dirname(os.path.abspath(__file__)), 'authservice.py'), '-a', 'amqps://127.0.0.1:%d' % cls.auth_service_port, '-c', os.getcwd()], expect=Process.RUNNING) + + policy_config_path = os.path.join(DIR, 'policy-authz') + + cls.router_port = cls.tester.get_port() + cls.tester.qdrouterd('router', Qdrouterd.Config([ + ('sslProfile', {'name':'myssl'}), + ('policy', {'maxConnections': 2, 'policyDir': policy_config_path, 'enableVhostPolicy': 'true'}), + # authService attribute has been deprecated. We are using it here to make sure that we are + # still backward compatible. + ('authServicePlugin', {'name':'myauth', 'sslProfile':'myssl', 'port': cls.auth_service_port, 'host': '127.0.0.1'}), + ('listener', {'host': '0.0.0.0', 'port': cls.router_port, 'role': 'normal', 'saslPlugin':'myauth', 'saslMechanisms':'SCRAM-SHA-1 PLAIN'}), + ('router', {'mode': 'standalone', 'id': 'router', + 'saslConfigName': 'tests-mech-SCRAM', + 'saslConfigPath': os.getcwd()}) + ])).wait_ready() + + @SkipIfNeeded(not SASL.extended(), "Cyrus library not available. skipping test") + def test_authorized(self): + container = Container() + client = ConnectionHandler('foo', 1) + container.connect("guest:[email protected]:%d" % self.router_port, handler=client) + container.run() + self.assertEqual(1, client.sent) + self.assertEqual(1, client.received) + self.assertEqual(0, len(client.errors)) + + @SkipIfNeeded(not SASL.extended(), "Cyrus library not available. skipping test") + def test_unauthorized(self): + container = Container() + client = ConnectionHandler('bar', 1) + container.connect("guest:[email protected]:%d" % self.router_port, handler=client) + container.run() + self.assertEqual(0, client.sent) + self.assertEqual(0, client.received) + self.assertEqual(2, len(client.errors)) + self.assertEqual('amqp:unauthorized-access', client.errors[0]) + self.assertEqual('amqp:unauthorized-access', client.errors[1]) + + @SkipIfNeeded(not SASL.extended(), "Cyrus library not available. skipping test") + def test_wildcard(self): + container = Container() + client = ConnectionHandler('whatever', 1) + container.connect("admin:[email protected]:%d" % self.router_port, handler=client) + container.run() + self.assertEqual(1, client.sent) + self.assertEqual(1, client.received) + self.assertEqual(0, len(client.errors)) + + @SkipIfNeeded(not SASL.extended(), "Cyrus library not available. skipping test") + def test_dynamic_source_anonymous_sender(self): + container = Container() + client = DynamicSourceAnonymousSender() + container.connect("admin:[email protected]:%d" % self.router_port, handler=client) + container.run() + self.assertEqual('hello', client.message) + self.assertEqual(0, len(client.errors)) + + +class AuthServicePluginAuthzDeprecatedTest(AuthServicePluginAuthzTest): + @classmethod + def setUpClass(cls): + """ + Tests the delegation of sasl auth to an external auth service. + """ + super(AuthServicePluginAuthzTest, cls).setUpClass() + + if not SASL.extended(): + return + + cls.createSaslFiles() + + cls.auth_service_port = cls.tester.get_port() + cls.tester.popen(['/usr/bin/env', '${PY_STRING}', os.path.join(os.path.dirname(os.path.abspath(__file__)), 'authservice.py'), '-a', 'amqps://127.0.0.1:%d' % cls.auth_service_port, '-c', os.getcwd()], expect=Process.RUNNING) + + cls.router_port = cls.tester.get_port() + cls.tester.qdrouterd('router', Qdrouterd.Config([ + ('sslProfile', {'name':'myssl'}), + # authService and authSslProfile attributea have been deprecated. + # We are using it here to make sure that we are backward compatible. + ('authServicePlugin', {'name':'myauth', 'authSslProfile':'myssl', 'authService': '127.0.0.1:%d' % cls.auth_service_port}), + ('listener', {'host': '0.0.0.0', 'port': cls.router_port, 'role': 'normal', 'saslPlugin':'myauth', 'saslMechanisms':'SCRAM-SHA-1 PLAIN'}), + ('router', {'mode': 'standalone', 'id': 'router', + 'saslConfigName': 'tests-mech-SCRAM', + 'saslConfigPath': os.getcwd()}) + ])).wait_ready() +class ConnectionHandler(MessagingHandler): + def __init__(self, address, count): + super(ConnectionHandler, self).__init__() + self.address = address + self.count = count + self.received = 0 + self.sent = 0 + self.errors = [] + + def on_message(self, event): + self.received += 1 + if self.received == self.count: + event.connection.close() + + def on_sendable(self, event): + if self.sent < self.count: + self.sent += 1 + event.sender.send(Message(body='msg-%s' %self.sent)) + + def on_link_error(self, event): + self.errors.append(event.link.remote_condition.name) + event.connection.close() + + def on_connection_opened(self, event): + event.container.create_receiver(event.connection, self.address) + event.container.create_sender(event.connection, self.address) + +class DynamicSourceAnonymousSender(MessagingHandler): + def __init__(self): + super(DynamicSourceAnonymousSender, self).__init__() + self.sender = None + self.message = None + self.errors = [] + + def on_message(self, event): + self.message = event.message.body; + event.connection.close() + + def on_link_opened(self, event): + if event.receiver: + self.sender.send(Message(address=event.receiver.remote_source.address, body='hello')) + + def on_connection_opened(self, event): + event.container.create_receiver(event.connection, None, dynamic=True) + self.sender = event.container.create_sender(event.connection, None) + +if __name__ == '__main__': + unittest.main(main_module()) + http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/bdd2aa38/tests/system_tests_handle_failover.py.in ---------------------------------------------------------------------- diff --git a/tests/system_tests_handle_failover.py.in b/tests/system_tests_handle_failover.py.in new file mode 100644 index 0000000..83ac620 --- /dev/null +++ b/tests/system_tests_handle_failover.py.in @@ -0,0 +1,309 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from __future__ import unicode_literals +from __future__ import division +from __future__ import absolute_import +from __future__ import print_function + +import os +from threading import Timer +import unittest2 as unittest +import json, re +from system_test import main_module, TIMEOUT +from system_test import TestCase, Qdrouterd, Process, TIMEOUT +from subprocess import PIPE, STDOUT + + +class FailoverTest(TestCase): + inter_router_port = None + + @classmethod + def router(cls, name, config): + config = Qdrouterd.Config(config) + + cls.routers.append(cls.tester.qdrouterd(name, config, wait=True)) + + @classmethod + def setUpClass(cls): + super(FailoverTest, cls).setUpClass() + + cls.routers = [] + + cls.inter_router_port = cls.tester.get_port() + cls.inter_router_port_1 = cls.tester.get_port() + cls.backup_port = cls.tester.get_port() + cls.backup_url = 'amqp://0.0.0.0:' + str(cls.backup_port) + cls.my_server_port = cls.tester.get_port() + + cls.failover_list = 'amqp://third-host:5671, ' + cls.backup_url + + # + # Router A tries to connect to Router B via its connectorToB. Router B responds with an open frame which will + # have the failover-server-list as one of its connection properties like the following - + # [0x13024d0]:0 <- @open(16) [container-id="Router.A", max-frame-size=16384, channel-max=32767, + # idle-time-out=8000, offered-capabilities=:"ANONYMOUS-RELAY", + # properties={:product="qpid-dispatch-router", :version="1.0.0", + # :"failover-server-list"=[{:"network-host"="some-host", :port="35000"}, + # {:"network-host"="0.0.0.0", :port="25000"}]}] + # + # The suite of tests determine if the router receiving this open frame stores it properly and if the + # original connection goes down, check that the router is trying to make connections to the failover urls. + # + FailoverTest.router('B', [ + ('router', {'mode': 'interior', 'id': 'B'}), + ('listener', {'host': '0.0.0.0', 'role': 'inter-router', 'port': cls.inter_router_port, + 'failoverUrls': cls.failover_list}), + ('listener', {'host': '0.0.0.0', 'role': 'normal', 'port': cls.tester.get_port()}), + ] + ) + + FailoverTest.router('A', + [ + ('router', {'mode': 'interior', 'id': 'A'}), + ('listener', {'host': '0.0.0.0', 'role': 'normal', 'port': cls.tester.get_port()}), + ('connector', {'name': 'connectorToB', 'role': 'inter-router', + 'port': cls.inter_router_port, 'verifyHostname': 'no'}), + ] + ) + + FailoverTest.router('C', [ + ('router', {'mode': 'interior', 'id': 'C'}), + ('listener', {'host': '0.0.0.0', 'role': 'inter-router', 'port': cls.backup_port}), + ('listener', {'host': '0.0.0.0', 'role': 'normal', 'port': cls.tester.get_port()}), + ] + ) + + cls.routers[1].wait_router_connected('B') + + def __init__(self, test_method): + TestCase.__init__(self, test_method) + self.success = False + self.timer_delay = 2 + self.max_attempts = 10 + self.attempts = 0 + + def address(self): + return self.routers[1].addresses[0] + + def run_qdmanage(self, cmd, input=None, expect=Process.EXIT_OK, address=None): + p = self.popen( + ['qdmanage'] + cmd.split(' ') + ['--bus', address or self.address(), '--indent=-1', '--timeout', str(TIMEOUT)], + stdin=PIPE, stdout=PIPE, stderr=STDOUT, expect=expect, + universal_newlines=True) + out = p.communicate(input)[0] + try: + p.teardown() + except Exception as e: + raise Exception("%s\n%s" % (e, out)) + return out + + def run_qdstat(self, args, regexp=None, address=None): + p = self.popen( + ['qdstat', '--bus', str(address or self.router.addresses[0]), '--timeout', str(TIMEOUT) ] + args, + name='qdstat-'+self.id(), stdout=PIPE, expect=None, + universal_newlines=True) + + out = p.communicate()[0] + assert p.returncode == 0, \ + "qdstat exit status %s, output:\n%s" % (p.returncode, out) + if regexp: assert re.search(regexp, out, re.I), "Can't find '%s' in '%s'" % (regexp, out) + return out + + def test_1_connector_has_failover_list(self): + """ + This is the most simple and straightforward case. Router A connects to Router B. Router B sends + failover information to Router A. + We make a qdmanage connector query to Router A which checks if Router A is storing the failover information + received from Router B.The failover list must consist of the original connection info (from the connector) + followed by the two items sent by the Router B (stored in cls.failover_list) + The 'failoverUrls' is comma separated. + """ + long_type = 'org.apache.qpid.dispatch.connector' + query_command = 'QUERY --type=' + long_type + output = json.loads(self.run_qdmanage(query_command)) + expected = "amqp://127.0.0.1:" + str(FailoverTest.inter_router_port) + ", " + FailoverTest.failover_list + + self.assertEqual(expected, output[0]['failoverUrls']) + + def schedule_B_to_C_failover_test(self): + if self.attempts < self.max_attempts: + if not self.success: + Timer(self.timer_delay, self.check_C_connector).start() + self.attempts += 1 + + def check_C_connector(self): + long_type = 'org.apache.qpid.dispatch.connector' + query_command = 'QUERY --type=' + long_type + output = json.loads(self.run_qdmanage(query_command, address=self.routers[1].addresses[0])) + + expected = FailoverTest.backup_url + ", " + "amqp://127.0.0.1:" + str(FailoverTest.inter_router_port) \ + + ", " + "amqp://third-host:5671" + + if output[0].get('failoverUrls') == expected: + self.success = True + else: + self.schedule_B_to_C_failover_test() + + def can_terminate(self): + if self.attempts == self.max_attempts: + return True + + if self.success: + return True + + return False + + def test_2_remove_router_B(self): + """ + In this test, we are killing Router B. As a result, Router A should try to connect to Router C. + Router C does NOT have a failover list, so the open frame that Router C sends to Router A will not contain + the failover-server-list property..Hence the failoverUrls list will remain unchanged except that the order of + the URLs would be different. + """ + + # First make sure there are no inter-router connections on router C + outs = self.run_qdstat(['--connections'], address=self.routers[2].addresses[1]) + + inter_router = 'inter-router' in outs + self.assertFalse(inter_router) + + # Kill the router B + FailoverTest.routers[0].teardown() + + # Schedule a test to make sure that the failover url is available + # and Router C has an inter-router connection + self.schedule_B_to_C_failover_test() + + while not self.can_terminate(): + pass + + self.assertTrue(self.success) + + + def schedule_C_to_B_failover_test(self): + if self.attempts < self.max_attempts: + if not self.success: + Timer(self.timer_delay, self.check_B_connector).start() + self.attempts += 1 + + def check_B_connector(self): + # Router A should now try to connect to Router B again since we killed Router C. + long_type = 'org.apache.qpid.dispatch.connector' + query_command = 'QUERY --type=' + long_type + output = json.loads(self.run_qdmanage(query_command, address=self.routers[1].addresses[0])) + + # The order that the URLs appear in the failoverUrls is important. This is the order in which the router + # will attempt to make connections in case the existing connection goes down. + + expected = "amqp://127.0.0.1:" + str(FailoverTest.inter_router_port) + ", " + \ + FailoverTest.failover_list + \ + ', amqp://127.0.0.1:%d' % FailoverTest.my_server_port + + if output[0].get('failoverUrls') == expected: + self.success = True + else: + self.schedule_C_to_B_failover_test() + + def test_3_reinstate_router_B(self): + """ + In this test, we are restarting Router B and killing Router C. Router A should now try to connect back to + Router B since it maintains the original connection info to Router B from the connector config information. + Before starting Router B back again, we + have a small config change to Router B wherein we are adding a new failover url to the original list. + This new failover url + points to our own server which will accept connections. This server will actually be used in the next test + but this test maskes sure that the new server url also shows up in the failoverUrls list. + """ + FailoverTest.router('B', [ + ('router', {'mode': 'interior', 'id': 'B'}), + ('listener', {'host': '0.0.0.0', 'role': 'inter-router', 'port': FailoverTest.inter_router_port, + 'failoverUrls': FailoverTest.failover_list + ', amqp://127.0.0.1:%d' % FailoverTest.my_server_port}), + ('listener', {'host': '0.0.0.0', 'role': 'normal', 'port': FailoverTest.tester.get_port()}), + ]) + + FailoverTest.routers[3].wait_ready() + + # Kill the router C. + # Now since Router B is up and running, router A should try to re-connect to Router B. + # This will prove that the router A is preserving the original connector information specified in its config. + FailoverTest.routers[2].teardown() + + self.success = False + self.attempts = 0 + + # Schedule a test to make sure that the failover url is available + self.schedule_C_to_B_failover_test() + + while not self.can_terminate(): + pass + + self.assertTrue(self.success) + + def check_A_connector(self): + # Router A should now try to connect to Router B again since we killed Router C. + long_type = 'org.apache.qpid.dispatch.connector' + query_command = 'QUERY --type=' + long_type + output = json.loads(self.run_qdmanage(query_command, address=self.routers[1].addresses[0])) + + # The order that the URLs appear in the failoverUrls is important. This is the order in which the router + # will attempt to make connections in case the existing connection goes down. + expected = 'amqp://127.0.0.1:%d' % FailoverTest.my_server_port + ", " + "amqp://127.0.0.1:" + str(FailoverTest.inter_router_port) + + if output[0].get('failoverUrls') == expected: + self.success = True + else: + self.schedule_B_to_my_server_failover_test() + + def schedule_B_to_my_server_failover_test(self): + if self.attempts < self.max_attempts: + if not self.success: + Timer(self.timer_delay, self.check_A_connector).start() + self.attempts += 1 + + def test_4_remove_router_B_connect_to_my_server(self): + """ + This test kills Router B again and makes sure that Router A now connects to our custom server that + accepts connections. This custom server intentionally sends an empty list for failover-server-list + Router A must look at this empty list and wipe out all failover information except the original connector information + and the current connection info. + """ + + + # Start MyServer + proc = FailoverTest.tester.popen( + ['/usr/bin/env', '${PY_STRING}', os.path.join(os.path.dirname(os.path.abspath(__file__)), 'failoverserver.py'), '-a', + 'amqp://127.0.0.1:%d' % FailoverTest.my_server_port], expect=Process.RUNNING) + + # Kill the router B again + FailoverTest.routers[3].teardown() + + self.success = False + self.attempts = 0 + + self.schedule_B_to_my_server_failover_test() + + while not self.can_terminate(): + pass + + self.assertTrue(self.success) + + +if __name__ == '__main__': + unittest.main(main_module()) --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
