264 lines
8.2 KiB
Python
264 lines
8.2 KiB
Python
#
|
|
# 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.
|
|
#
|
|
|
|
import gc
|
|
import time
|
|
|
|
try:
|
|
from proton import VERSION_MAJOR, VERSION_MINOR
|
|
except ImportError:
|
|
# older proton did not export version info
|
|
VERSION_MAJOR = 0
|
|
VERSION_MINOR = 0
|
|
|
|
import pyngus
|
|
|
|
|
|
class Test(object):
|
|
|
|
PROTON_VERSION = (int(VERSION_MAJOR), int(VERSION_MINOR))
|
|
|
|
def __init__(self, name):
|
|
self.name = name
|
|
|
|
def configure(self, config):
|
|
self.config = config
|
|
|
|
def default(self, name, value, **profiles):
|
|
default = value
|
|
profile = self.config.defines.get("profile")
|
|
if profile:
|
|
default = profiles.get(profile, default)
|
|
return self.config.defines.get(name, default)
|
|
|
|
def setup(self):
|
|
gc.enable()
|
|
gc.collect()
|
|
assert not gc.garbage, "Object leak: %s" % str(gc.garbage)
|
|
|
|
def teardown(self):
|
|
gc.collect()
|
|
assert not gc.garbage, "Object leak: %s" % str(gc.garbage)
|
|
|
|
@property
|
|
def delay(self):
|
|
return float(self.default("delay", "1", fast="0.1"))
|
|
|
|
@property
|
|
def timeout(self):
|
|
return float(self.default("timeout", "60", fast="10"))
|
|
|
|
@property
|
|
def verbose(self):
|
|
return int(self.default("verbose", 0))
|
|
|
|
|
|
class Skipped(Exception):
|
|
"""Throw to skip a test without generating a test failure."""
|
|
skipped = True
|
|
|
|
|
|
def do_connection_io(c1, c2):
|
|
"""Transfer data between two Connection objects, return True if I/O
|
|
occured."""
|
|
def _do_io(src, dst):
|
|
count = min(src.has_output, dst.needs_input)
|
|
if count > 0:
|
|
count = dst.process_input(src.output_data())
|
|
if count > 0:
|
|
src.output_written(count)
|
|
return count
|
|
|
|
o1 = _do_io(c1, c2)
|
|
o2 = _do_io(c2, c1)
|
|
return o1 > 0 or o2 > 0
|
|
|
|
|
|
def process_connections(c1, c2, timestamp=None):
|
|
"""Do I/O and protocol processing on two connected Connections until no
|
|
further data is transferred."""
|
|
if timestamp is None:
|
|
timestamp = time.time()
|
|
c1.process(timestamp)
|
|
c2.process(timestamp)
|
|
while do_connection_io(c1, c2):
|
|
c1.process(timestamp)
|
|
c2.process(timestamp)
|
|
|
|
|
|
def _validate_conn_callback(connection):
|
|
"""Callbacks must only occur when holding the Connection callback lock"""
|
|
assert connection._callback_lock.in_callback, \
|
|
connection._callback_lock.in_callback
|
|
|
|
|
|
def _validate_link_callback(link):
|
|
"""Callbacks must only occur when holding the Link callback lock."""
|
|
assert link._callback_lock.in_callback, link._callback_lock.in_callback
|
|
|
|
|
|
class ConnCallback(pyngus.ConnectionEventHandler):
|
|
"""Caches the callback parameters for processing by a test."""
|
|
class RequestArgs(object):
|
|
def __init__(self, handle, name, source, target, props):
|
|
self.link_handle = handle
|
|
self.name = name
|
|
self.requested_source = source
|
|
self.requested_target = target
|
|
self.properties = props
|
|
|
|
def __init__(self):
|
|
self.active_ct = 0
|
|
self.failed_ct = 0
|
|
self.failed_error = None
|
|
self.remote_closed_ct = 0
|
|
self.remote_closed_error = None
|
|
self.closed_ct = 0
|
|
self.sender_requested_ct = 0
|
|
self.sender_requested_args = []
|
|
self.receiver_requested_ct = 0
|
|
self.receiver_requested_args = []
|
|
|
|
self.sasl_step_ct = 0
|
|
self.sasl_done_ct = 0
|
|
self.sasl_done_outcome = None
|
|
|
|
def connection_active(self, connection):
|
|
_validate_conn_callback(connection)
|
|
self.active_ct += 1
|
|
|
|
def connection_failed(self, connection, error):
|
|
_validate_conn_callback(connection)
|
|
self.failed_ct += 1
|
|
self.failed_error = error
|
|
|
|
def connection_remote_closed(self, connection, error=None):
|
|
_validate_conn_callback(connection)
|
|
self.remote_closed_ct += 1
|
|
self.remote_closed_error = error
|
|
|
|
def connection_closed(self, connection):
|
|
_validate_conn_callback(connection)
|
|
self.closed_ct += 1
|
|
|
|
def sender_requested(self, connection, link_handle,
|
|
name, requested_source,
|
|
properties):
|
|
_validate_conn_callback(connection)
|
|
self.sender_requested_ct += 1
|
|
args = ConnCallback.RequestArgs(link_handle, name,
|
|
requested_source, None,
|
|
properties)
|
|
self.sender_requested_args.append(args)
|
|
|
|
def receiver_requested(self, connection, link_handle,
|
|
name, requested_target,
|
|
properties):
|
|
_validate_conn_callback(connection)
|
|
self.receiver_requested_ct += 1
|
|
args = ConnCallback.RequestArgs(link_handle, name,
|
|
None, requested_target,
|
|
properties)
|
|
self.receiver_requested_args.append(args)
|
|
|
|
def sasl_step(self, connection, pn_sasl):
|
|
_validate_conn_callback(connection)
|
|
self.sasl_step_ct += 1
|
|
|
|
def sasl_done(self, connection, pn_sasl, result):
|
|
_validate_conn_callback(connection)
|
|
self.sasl_done_ct += 1
|
|
self.sasl_done_outcome = result
|
|
|
|
|
|
class SenderCallback(pyngus.SenderEventHandler):
|
|
def __init__(self):
|
|
self.active_ct = 0
|
|
self.remote_closed_ct = 0
|
|
self.remote_closed_error = None
|
|
self.closed_ct = 0
|
|
self.credit_granted_ct = 0
|
|
|
|
def sender_active(self, sender_link):
|
|
_validate_link_callback(sender_link)
|
|
self.active_ct += 1
|
|
|
|
def sender_remote_closed(self, sender_link, error=None):
|
|
_validate_link_callback(sender_link)
|
|
self.remote_closed_ct += 1
|
|
self.remote_closed_error = error
|
|
|
|
def sender_closed(self, sender_link):
|
|
_validate_link_callback(sender_link)
|
|
self.closed_ct += 1
|
|
|
|
def credit_granted(self, sender_link):
|
|
_validate_link_callback(sender_link)
|
|
self.credit_granted_ct += 1
|
|
|
|
|
|
class DeliveryCallback(object):
|
|
"""Capture the message delivery callback for a sent message."""
|
|
def __init__(self):
|
|
self.link = None
|
|
self.handle = None
|
|
self.status = None
|
|
self.info = None
|
|
self.count = 0
|
|
|
|
def __call__(self, link, handle, status, info):
|
|
_validate_link_callback(link)
|
|
self.link = link
|
|
self.handle = handle
|
|
self.status = status
|
|
self.info = info
|
|
self.count += 1
|
|
|
|
|
|
class ReceiverCallback(pyngus.ReceiverEventHandler):
|
|
def __init__(self):
|
|
self.active_ct = 0
|
|
self.remote_closed_ct = 0
|
|
self.remote_closed_error = None
|
|
self.closed_ct = 0
|
|
self.message_received_ct = 0
|
|
self.received_messages = []
|
|
|
|
def receiver_active(self, receiver_link):
|
|
_validate_link_callback(receiver_link)
|
|
_validate_conn_callback(receiver_link.connection)
|
|
self.active_ct += 1
|
|
|
|
def receiver_remote_closed(self, receiver_link, error=None):
|
|
_validate_link_callback(receiver_link)
|
|
_validate_conn_callback(receiver_link.connection)
|
|
self.remote_closed_ct += 1
|
|
self.remote_closed_error = error
|
|
|
|
def receiver_closed(self, receiver_link):
|
|
_validate_link_callback(receiver_link)
|
|
_validate_conn_callback(receiver_link.connection)
|
|
self.closed_ct += 1
|
|
|
|
def message_received(self, receiver_link, message, handle):
|
|
_validate_link_callback(receiver_link)
|
|
_validate_conn_callback(receiver_link.connection)
|
|
self.message_received_ct += 1
|
|
self.received_messages.append((message, handle))
|