You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by gm...@apache.org on 2020/10/13 13:49:04 UTC
[qpid-dispatch] branch dev-protocol-adaptors updated:
DISPATCH-1743: Added http2 system tests. Uses Quart to launch a server and
curl as the client
This is an automated email from the ASF dual-hosted git repository.
gmurthy pushed a commit to branch dev-protocol-adaptors
in repository https://gitbox.apache.org/repos/asf/qpid-dispatch.git
The following commit(s) were added to refs/heads/dev-protocol-adaptors by this push:
new 3c726ba DISPATCH-1743: Added http2 system tests. Uses Quart to launch a server and curl as the client
3c726ba is described below
commit 3c726bad51eed116bb70a04fe1910ab96040064b
Author: Ganesh Murthy <gm...@apache.org>
AuthorDate: Mon Oct 12 11:55:28 2020 -0400
DISPATCH-1743: Added http2 system tests. Uses Quart to launch a server and curl as the client
---
.travis.yml | 4 +-
LICENSE | 4 +
README | 11 ++
dockerfiles/Dockerfile-fedora | 9 +-
dockerfiles/Dockerfile-ubuntu | 7 +-
tests/http2_server.py | 68 ++++++++++-
tests/images/apache.jpg | Bin 0 -> 5147 bytes
tests/images/balanced-routing.png | Bin 0 -> 45011 bytes
tests/quart/LICENSE | 22 ++++
tests/system_test.py | 9 +-
tests/system_tests_http2.py | 247 +++++++++++++++++++++++++++++++++++---
11 files changed, 353 insertions(+), 28 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index ab84fe7..61b28ce 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -67,6 +67,8 @@ jobs:
# https://github.com/pypa/virtualenv/issues/1873
- python -m pip install --user --upgrade pip
- python -m pip install --user --upgrade tox virtualenv==20.0.23
+ # Install quart to run the http2 tests.
+ - python -m pip install --user quart
env:
- CC=clang-10
- CXX=clang++-10
@@ -101,6 +103,7 @@ addons:
- python2.7-dev
- sasl2-bin
- swig
+ - libnghttp2-dev
# documentation
- asciidoc
- asciidoctor
@@ -110,7 +113,6 @@ addons:
- tox
# code coverage
- lcov
- - libnghttp2-dev
install:
- NPROC=2
diff --git a/LICENSE b/LICENSE
index 2569aa5..6ab2e63 100644
--- a/LICENSE
+++ b/LICENSE
@@ -209,3 +209,7 @@ This product bundles a copy of DOCTEST within its test code (found at
tests/c_unittests/doctest.h), which is distributed under the MIT licence.
A copy of its licence can be found at: tests/c_unittests/LICENSE.txt
+
+The tests run a http2 server which uses a Python ASGI web microframework
+called Quart which is distributed under the MIT licence.
+A copy of Quart's licence can be found at: tests/quart/LICENSE
diff --git a/README b/README
index 453694b..635a8ff 100644
--- a/README
+++ b/README
@@ -68,6 +68,17 @@ $ ./run.py -m unittest system_tests_qdstat
Run it without arguments to get a summary of how it can be used:
$ ./run.py
+The HTTP2 system tests (tests/system_tests_http2.py) use the Python Quart framework to start a HTTP2 server.
+The HTTP2 system tests will run only if
+ 1. Python version >= 3.7
+ 2. Python Web Microframework Quart version >= 0.13
+ 3. curl is available
+
+To install pip and Quart
+ - curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
+ - python3 get-pip.py
+ - pip3 install --user quart
+
Test Suite Code Coverage (GNU tools only)
=========================================
diff --git a/dockerfiles/Dockerfile-fedora b/dockerfiles/Dockerfile-fedora
index 685e4c9..72b7f18 100644
--- a/dockerfiles/Dockerfile-fedora
+++ b/dockerfiles/Dockerfile-fedora
@@ -30,11 +30,16 @@ FROM fedora:latest
MAINTAINER "dev@qpid.apache.org"
# Install required packages. Some in this list are from proton's INSTALL.md (https://github.com/apache/qpid-proton/blob/master/INSTALL.md) and the rest are from dispatch (https://github.com/apache/qpid-dispatch/blob/master/README)
-RUN dnf -y install gcc gcc-c++ cmake openssl-devel cyrus-sasl-devel cyrus-sasl-plain cyrus-sasl-gssapi cyrus-sasl-md5 swig java-1.8.0-openjdk-devel git make valgrind emacs libwebsockets-devel python-devel
+RUN dnf -y install gcc gcc-c++ cmake openssl-devel cyrus-sasl-devel cyrus-sasl-plain cyrus-sasl-gssapi cyrus-sasl-md5 swig java-1.8.0-openjdk-devel git make valgrind emacs libwebsockets-devel python-devel libnghttp2-devel curl
+
+RUN curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
+RUN python3 get-pip.py
+RUN pip3 install --user quart
# Create a main directory and clone the qpid-proton repo from github
RUN mkdir /main && cd /main && git clone https://gitbox.apache.org/repos/asf/qpid-proton.git && cd /main/qpid-proton && mkdir /main/qpid-proton/build
+WORKDIR /main
WORKDIR /main/qpid-proton/build
# make and install proton
@@ -51,4 +56,4 @@ RUN cmake .. -DCMAKE_INSTALL_PREFIX=/usr && make install
# Start the dispatch router
ENTRYPOINT ["qdrouterd"]
-# CMD ["/bin/bash"]
+#CMD ["/bin/bash"]
diff --git a/dockerfiles/Dockerfile-ubuntu b/dockerfiles/Dockerfile-ubuntu
index be6b995..dde2c8b 100644
--- a/dockerfiles/Dockerfile-ubuntu
+++ b/dockerfiles/Dockerfile-ubuntu
@@ -26,9 +26,14 @@ MAINTAINER "dev@qpid.apache.org"
ARG DEBIAN_FRONTEND=noninteractive
# Install all the required packages. Some in this list were picked off from proton's INSTALL.md (https://github.com/apache/qpid-proton/blob/master/INSTALL.md) and the rest are from dispatch (https://github.com/apache/qpid-dispatch/blob/master/README)
RUN apt-get update && \
- apt-get install -y gcc g++ automake libwebsockets-dev libtool zlib1g-dev cmake libsasl2-dev libssl-dev python python-dev libuv1-dev sasl2-bin swig maven git && \
+ apt-get install -y curl gcc g++ automake libwebsockets-dev libtool zlib1g-dev cmake libsasl2-dev libssl-dev libnghttp2-dev python3-dev libuv1-dev sasl2-bin swig maven git && \
apt-get -y clean
+RUN curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
+RUN python3 get-pip.py
+RUN pip3 install --user quart
+
+
RUN git clone https://gitbox.apache.org/repos/asf/qpid-dispatch.git && cd /qpid-dispatch && git submodule add https://gitbox.apache.org/repos/asf/qpid-proton.git && git submodule update --init
WORKDIR /qpid-dispatch
diff --git a/tests/http2_server.py b/tests/http2_server.py
index f28b572..c56dd2a 100644
--- a/tests/http2_server.py
+++ b/tests/http2_server.py
@@ -16,15 +16,75 @@
# specific language governing permissions and limitations
# under the License.
#
+import os
+from quart import Quart, request
+from quart.static import send_file
+from quart.exceptions import HTTPStatusException
+import json
+app = Quart(__name__)
+import system_test
+class MyInfo(object):
+ def __init__(self, fname, lname, id=None):
+ self.fname = fname
+ self.lname = lname
+ self.id = id
+ #self.hobby = None
+ #self.style = None
+my_info = MyInfo(fname="John", lname="Doe")
-from quart import Quart
+def image_file(name):
+ return os.path.join(system_test.DIR, 'images', name)
-app = Quart(__name__)
+@app.route("/myinfo/delete/<id>", methods=["DELETE"])
+async def delete_myinfo(id):
+ my_info.id = id
+ jsonStr = json.dumps(my_info.__dict__)
+ return jsonStr
+
+@app.route('/myinfo', methods=['GET', 'POST', 'PUT'])
+async def create_myinfo():
+ form = await request.form
+ name = form['fname']
+ age = form['lname']
+ message = "Success! Your first name is %s, last name is %s" % (name, age)
+ return message
+
+def large_string(length):
+ i = 0
+ ret_string = ""
+ while (i < length):
+ ret_string += str(i) + ","
+ i += 1
+ return ret_string
@app.route('/')
async def index():
- return 'Hello World'
+ return large_string(1000)
+
+@app.route('/largeget', methods=['GET'])
+async def largeget():
+ return large_string(50000)
+
+@app.route('/patch', methods=['PATCH'])
+async def patch():
+ data = await request.data
+ return data
+
+# Return a 500 error, "Service Unavailable"
+@app.route('/test/500')
+async def service_unavailable():
+ raise HTTPStatusException()
+
+@app.route('/images/balanced-routing.png', methods=['GET'])
+async def get_png_images():
+ img_file = image_file("balanced-routing.png")
+ return await send_file(img_file, mimetype='image/png')
+
+@app.route('/images/apache.jpg', methods=['GET'])
+async def get_jpg_images():
+ img_file = image_file("apache.jpg")
+ return await send_file(img_file, mimetype='image/jpg')
#app.run(port=5000, certfile='cert.pem', keyfile='key.pem')
-app.run(port=5000)
+app.run(port=os.getenv('SERVER_LISTEN_PORT'))
diff --git a/tests/images/apache.jpg b/tests/images/apache.jpg
new file mode 100644
index 0000000..2475669
Binary files /dev/null and b/tests/images/apache.jpg differ
diff --git a/tests/images/balanced-routing.png b/tests/images/balanced-routing.png
new file mode 100644
index 0000000..3c2e018
Binary files /dev/null and b/tests/images/balanced-routing.png differ
diff --git a/tests/quart/LICENSE b/tests/quart/LICENSE
new file mode 100644
index 0000000..6417781
--- /dev/null
+++ b/tests/quart/LICENSE
@@ -0,0 +1,22 @@
+Copyright P G Jones 2017.
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/tests/system_test.py b/tests/system_test.py
index c633b56..521ce9f 100755
--- a/tests/system_test.py
+++ b/tests/system_test.py
@@ -607,6 +607,12 @@ class Qdrouterd(Process):
raise Exception("Unknown protocol family: %s" % protocol_family)
@property
+ def http_addresses(self):
+ """Return http://host:port addresses for all http listeners"""
+ cfg = self.config.sections('httpListener')
+ return ["http://%s" % self._cfg_2_host_port(l) for l in cfg]
+
+ @property
def addresses(self):
"""Return amqp://host:port addresses for all listeners"""
cfg = self.config.sections('listener')
@@ -770,7 +776,7 @@ class Tester(object):
"""Return a Qdrouterd that will be cleaned up on teardown"""
return self.cleanup(Qdrouterd(*args, **kwargs))
- def httpserver(self, *args, **kwargs):
+ def http2server(self, *args, **kwargs):
return self.cleanup(Http2Server(*args, **kwargs))
port_range = (20000, 30000)
@@ -1113,7 +1119,6 @@ class AsyncTestSender(MessagingHandler):
self._conn.close()
self._conn = None
-
class QdManager(object):
"""
A means to invoke qdmanage during a testcase
diff --git a/tests/system_tests_http2.py b/tests/system_tests_http2.py
index b1ca195..3dd0664 100644
--- a/tests/system_tests_http2.py
+++ b/tests/system_tests_http2.py
@@ -17,36 +17,247 @@
# under the License.
#
-import os
+import os, sys
from time import sleep
+import system_test
+from system_test import TestCase, Qdrouterd, Process, SkipIfNeeded
+from subprocess import PIPE
-from system_test import TestCase, Qdrouterd, Process
+def python_37_available():
+ if sys.version_info >= (3, 7):
+ return True
-class Http2Test(TestCase):
+def curl_available():
+ popen_args = ['curl', '--version']
+ try:
+ process = Process(popen_args,
+ name='curl_check',
+ stdout=PIPE,
+ expect=None,
+ universal_newlines=True)
+ out = process.communicate()[0]
+ return True
+ except:
+ return False
+
+def quart_available():
+ popen_args = ['quart', '--version']
+ try:
+ process = Process(popen_args,
+ name='curl_check',
+ stdout=PIPE,
+ expect=None,
+ universal_newlines=True)
+ out = process.communicate()[0]
+ parts = out.split(".")
+ if int(parts[1]) >= 13:
+ return True
+ return False
+ except Exception as e:
+ print (e)
+ print("quart_not_available")
+ return False
+
+def skip_test():
+ if python_37_available() and quart_available() and curl_available():
+ return False
+ return True
+
+class Http2TestBase(TestCase):
+ def run_curl(self, args=None, regexp=None, address=None):
+ # Tell with -m / --max-time the maximum time, in seconds, that you
+ # allow the command line to spend before curl exits with a
+ # timeout error code (28).
+ local_args = ["--http2-prior-knowledge"]
+ if args:
+ local_args = args + ["--http2-prior-knowledge"]
+
+ popen_args = ['curl',
+ str(address),
+ '--max-time', str(system_test.TIMEOUT)] + local_args
+ p = self.popen(popen_args,
+ name='curl-' + self.id(), stdout=PIPE, expect=None,
+ universal_newlines=True)
+
+ out = p.communicate()[0]
+ assert (p.returncode == 0)
+ return out
+
+
+class CommonHttp2Tests():
+ """
+ The tests in this class are run by both Http2TestOneRouter and
+ Http2TestTwoRouter
+ """
+ @SkipIfNeeded(skip_test(), "Python 3.7 or greater, Quart 0.13.0 or greater and curl needed to run http2 tests")
+ # Tests the HTTP2 head request
+ def test_head_request(self):
+ # Run curl 127.0.0.1:port --http2-prior-knowledge --head
+ address = self.router_qdra.http_addresses[0]
+ out = self.run_curl(args=["--head"], address=address)
+ self.assertIn('HTTP/2 200', out)
+ self.assertIn('server: hypercorn-h2', out)
+ self.assertIn('content-type: text/html; charset=utf-8', out)
+
+ @SkipIfNeeded(skip_test(), "Python 3.7 or greater, Quart 0.13.0 or greater and curl needed to run http2 tests")
+ def test_get_request(self):
+ # Run curl 127.0.0.1:port --http2-prior-knowledge
+ address = self.router_qdra.http_addresses[0]
+ out = self.run_curl(address=address)
+ i = 0
+ ret_string = ""
+ while (i < 1000):
+ ret_string += str(i) + ","
+ i += 1
+ self.assertIn(ret_string, out)
+
+ @SkipIfNeeded(skip_test(), "Python 3.7 or greater, Quart 0.13.0 or greater and curl needed to run http2 tests")
+ def test_large_get_request(self):
+ # Tests a large get request. Response is more than 50k which means it
+ # will span many qd_http2_buffer_t objects.
+ # Run curl 127.0.0.1:port/largeget --http2-prior-knowledge
+ address = self.router_qdra.http_addresses[0] + "/largeget"
+ out = self.run_curl(address=address)
+ self.assertIn("49996,49997,49998,49999", out)
+
+ @SkipIfNeeded(skip_test(), "Python 3.7 or greater, Quart 0.13.0 or greater and curl needed to run http2 tests")
+ def test_post_request(self):
+ # curl -d "fname=John&lname=Doe" -X POST 127.0.0.1:9000/myinfo --http2-prior-knowledge
+ address = self.router_qdra.http_addresses[0] + "/myinfo"
+ out = self.run_curl(args=['-d', 'fname=John&lname=Doe', '-X', 'POST'], address=address)
+ self.assertIn('Success! Your first name is John, last name is Doe', out)
+
+ @SkipIfNeeded(skip_test(), "Python 3.7 or greater, Quart 0.13.0 or greater and curl needed to run http2 tests")
+ def test_delete_request(self):
+ # curl -X DELETE "http://127.0.0.1:9000/myinfo/delete/22122" -H "accept: application/json" --http2-prior-knowledge
+ address = self.router_qdra.http_addresses[0] + "/myinfo/delete/22122"
+ out = self.run_curl(args=['-X', 'DELETE'], address=address)
+ self.assertIn('{"fname": "John", "lname": "Doe", "id": "22122"}', out)
+
+
+ @SkipIfNeeded(skip_test(), "Python 3.7 or greater, Quart 0.13.0 or greater and curl needed to run http2 tests")
+ def test_put_request(self):
+ # curl -d "fname=John&lname=Doe" -X PUT 127.0.0.1:9000/myinfo --http2-prior-knowledge
+ address = self.router_qdra.http_addresses[0] + "/myinfo"
+ out = self.run_curl(args=['-d', 'fname=John&lname=Doe', '-X', 'PUT'], address=address)
+ self.assertIn('Success! Your first name is John, last name is Doe', out)
+
+ @SkipIfNeeded(skip_test(), "Python 3.7 or greater, Quart 0.13.0 or greater and curl needed to run http2 tests")
+ def test_patch_request(self):
+ # curl -d "fname=John&lname=Doe" -X PATCH 127.0.0.1:9000/myinfo --http2-prior-knowledge
+ address = self.router_qdra.http_addresses[0] + "/patch"
+ out = self.run_curl(args=['--data', '{\"op\":\"add\",\"path\":\"/user\",\"value\":\"jane\"}', '-X', 'PATCH'], address=address)
+ self.assertIn('"op":"add"', out)
+
+ @SkipIfNeeded(skip_test(), "Python 3.7 or greater, Quart 0.13.0 or greater and curl needed to run http2 tests")
+ def test_404(self):
+ # Run curl 127.0.0.1:port/unavilable --http2-prior-knowledge
+ address = self.router_qdra.http_addresses[0] + "/unavilable"
+ out = self.run_curl(address=address)
+ self.assertIn('404 Not Found', out)
+
+ @SkipIfNeeded(skip_test(), "Python 3.7 or greater, Quart 0.13.0 or greater and curl needed to run http2 tests")
+ def test_500(self):
+ # Run curl 127.0.0.1:port/unavilable --http2-prior-knowledge
+ address = self.router_qdra.http_addresses[0] + "/test/500"
+ out = self.run_curl(address=address)
+ self.assertIn('500 Internal Server Error', out)
+
+ @SkipIfNeeded(skip_test(), "Python 3.7 or greater, Quart 0.13.0 or greater and curl needed to run http2 tests")
+ def test_get_image_png(self):
+ # Run curl 127.0.0.1:port --http2-prior-knowledge
+ passed = False
+ try:
+ address = self.router_qdra.http_addresses[0] + "/images/balanced-routing.png"
+ self.run_curl(address=address)
+ except UnicodeDecodeError as u:
+ if "codec can't decode byte 0x89" in str(u):
+ passed = True
+ self.assertTrue(passed)
+
+ @SkipIfNeeded(skip_test(), "Python 3.7 or greater, Quart 0.13.0 or greater and curl needed to run http2 tests")
+ def test_get_image_jpg(self):
+ # Run curl 127.0.0.1:port --http2-prior-knowledge
+ passed = False
+ try:
+ address = self.router_qdra.http_addresses[0] + "/images/apache.jpg"
+ self.run_curl(address=address)
+ except UnicodeDecodeError as u:
+ print (u)
+ if "codec can't decode byte 0xff" in str(u):
+ passed = True
+ self.assertTrue(passed)
+
+
+class Http2TestOneRouter(Http2TestBase, CommonHttp2Tests):
@classmethod
def setUpClass(cls):
- super(Http2Test, cls).setUpClass()
+ super(Http2TestOneRouter, cls).setUpClass()
+ if skip_test():
+ return
+ cls.http2_server_name = "http2_server"
+ os.environ["QUART_APP"] = "http2server:app"
+ os.environ['SERVER_LISTEN_PORT'] = str(cls.tester.get_port())
+ cls.http2_server = cls.tester.http2server(name=cls.http2_server_name,
+ listen_port=int(os.getenv('SERVER_LISTEN_PORT')),
+ py_string='python3',
+ server_file="http2_server.py")
name = "http2-test-router"
- cls.router_http2_listen_port = cls.tester.get_port()
config = Qdrouterd.Config([
('router', {'mode': 'standalone', 'id': 'QDR'}),
- ('listener', {'port': cls.router_http2_listen_port, 'role': 'normal', 'host': '0.0.0.0'}),
- ('httpListener', {'port': cls.tester.get_port(), 'address': 'examples',
- 'host': '0.0.0.0'})
+ ('listener', {'port': cls.tester.get_port(), 'role': 'normal', 'host': '0.0.0.0'}),
+ ('httpListener', {'port': cls.tester.get_port(), 'address': 'examples',
+ 'host': '127.0.0.1', 'protocolVersion': 'HTTP2'}),
+ ('httpConnector',
+ {'port': os.getenv('SERVER_LISTEN_PORT'), 'address': 'examples',
+ 'host': '127.0.0.1', 'protocolVersion': 'HTTP2'})
])
- cls.router = cls.tester.qdrouterd(name, config, wait=True)
+ cls.router_qdra = cls.tester.qdrouterd(name, config, wait=True)
+
+
+
+class Http2TestTwoRouter(Http2TestBase, CommonHttp2Tests):
+ @classmethod
+ def setUpClass(cls):
+ super(Http2TestTwoRouter, cls).setUpClass()
+ if skip_test():
+ return
cls.http2_server_name = "http2_server"
- #cls.server_http2_listen_port = cls.tester.get_port()
- cls.server_http2_listen_port = 5000
os.environ["QUART_APP"] = "http2server:app"
- cls.http2_server = cls.tester.httpserver(name=cls.http2_server_name,
- listen_port=cls.server_http2_listen_port,
- py_string='python3',
- server_file="http2_server.py")
- sleep(5)
+ os.environ['SERVER_LISTEN_PORT'] = str(cls.tester.get_port())
+ cls.http2_server = cls.tester.http2server(name=cls.http2_server_name,
+ listen_port=int(os.getenv('SERVER_LISTEN_PORT')),
+ py_string='python3',
+ server_file="http2_server.py")
+ name = "http2-test-router"
+ inter_router_port = cls.tester.get_port()
- def test_head_request(self):
- pass
+ config_qdra = Qdrouterd.Config([
+ ('router', {'mode': 'interior', 'id': 'QDR.A'}),
+ ('listener', {'port': cls.tester.get_port(), 'role': 'normal', 'host': '0.0.0.0'}),
+ ('httpListener', {'port': cls.tester.get_port(), 'address': 'examples',
+ 'host': '127.0.0.1', 'protocolVersion': 'HTTP2'}),
+ ('listener', {'role': 'inter-router', 'port': inter_router_port})
+ ])
+
+ config_qdrb = Qdrouterd.Config([
+ ('router', {'mode': 'interior', 'id': 'QDR.B'}),
+ ('listener', {'port': cls.tester.get_port(), 'role': 'normal', 'host': '0.0.0.0'}),
+ ('httpConnector',
+ {'port': os.getenv('SERVER_LISTEN_PORT'), 'address': 'examples',
+ 'host': '127.0.0.1', 'protocolVersion': 'HTTP2'}),
+ ('connector', {'name': 'connectorToA', 'role': 'inter-router',
+ 'port': inter_router_port,
+ 'verifyHostname': 'no'})
+
+ ])
+
+ cls.router_qdra = cls.tester.qdrouterd(name, config_qdra, wait=True)
+ cls.router_qdrb = cls.tester.qdrouterd(name, config_qdrb, wait=True)
+
+ cls.router_qdra.wait_router_connected('QDR.B')
+ cls.router_qdrb.wait_router_connected('QDR.A')
+ sleep(2)
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org