class ZmqAuthTestCase(unittest.TestCase, BaseLeapTest): def setUp(self): self.factory = ZmqFactory() self._config_prefix = os.path.join(self.tempdir, "leap", "events") self.public, self.secret = maybe_create_and_get_certificates( self._config_prefix, 'server') self.authenticator = auth.TxAuthenticator(self.factory) self.authenticator.start() self.auth_req = auth.TxAuthenticationRequest(self.factory) def tearDown(self): self.factory.shutdown() def test_curve_auth(self): self.auth_req.start() self.auth_req.allow('127.0.0.1') public_keys_dir = os.path.join(self._config_prefix, PUBLIC_KEYS_PREFIX) self.auth_req.configure_curve(domain="*", location=public_keys_dir) def check(ignored): authenticator = self.authenticator.authenticator certs = authenticator.certs['*'] self.failUnlessEqual(authenticator.whitelist, set([u'127.0.0.1'])) self.failUnlessEqual(certs[certs.keys()[0]], True) return _wait(0.1).addCallback(check)
class ZmqConsumer(service.Service): def __init__(self, store, zmq_endpoints): self.store = store self.endpoints = zmq_endpoints self._socket = None self._factory = None def startService(self): self._factory = ZmqFactory() endpoints = [ ZmqEndpoint(ZmqEndpointType.connect, endpoint) for endpoint in self.endpoints ] log.msg("Configuring ZeroMQ subscription socket", logLevel=logging.DEBUG) for endpoint in endpoints: log.msg("Connecting to the {endpoint} ZeroMQ endpoint".format( endpoint=endpoint)) s = ZmqSubConnection(self._factory, endpoint) s.subscribe(b"") s.gotMessage = self.on_message log.msg("ZeroMQ consumer is ready") def on_message(self, body, topic): topic = topic.decode("utf-8") msg = json.loads(body) if "msg_id" not in msg: log.msg( "Received a message without a msg_id from ZeroMQ on topic {topic}" .format(topic=topic), logLevel=logging.INFO, ) return msg_id = msg["msg_id"] log.msg( "Received from ZeroMQ on topic {topic}: {msgid}".format( topic=topic, msgid=msg["msg_id"]), logLevel=logging.DEBUG, ) msg_id = YEAR_PREFIX_RE.sub("", msg_id) if msg_id in self.store: log.msg( "Received a duplicate ZeroMQ message with id {msg_id} on topic {topic}" .format(msg_id=msg_id, topic=topic), logLevel=logging.INFO, ) return self.store[msg_id] = (datetime.utcnow(), msg) def stopService(self): log.msg("Stopping ZmqConsumer", logLevel=logging.DEBUG) if self._factory.connections is not None: self._factory.shutdown()
class EventsGenericClientTestCase(object): def setUp(self): flags.set_events_enabled(True) self.factory = ZmqFactory() self._server = server.ensure_server(emit_addr="tcp://127.0.0.1:0", reg_addr="tcp://127.0.0.1:0", factory=self.factory, enable_curve=False) self._client.configure_client( emit_addr="tcp://127.0.0.1:%d" % self._server.pull_port, reg_addr="tcp://127.0.0.1:%d" % self._server.pub_port, factory=self.factory, enable_curve=False) def tearDown(self): flags.set_events_enabled(False) self.factory.shutdown() self._client.instance().reset() def test_client_register(self): """ Ensure clients can register callbacks. """ callbacks = self._client.instance().callbacks self.assertTrue( len(callbacks) == 0, 'There should be no callback for this event.') # register one event event1 = catalog.CLIENT_UID def cbk1(event, _): return True uid1 = self._client.register(event1, cbk1) # assert for correct registration self.assertTrue(len(callbacks) == 1) self.assertTrue(callbacks[event1][uid1] == cbk1, 'Could not register event in local client.') # register another event event2 = catalog.CLIENT_SESSION_ID def cbk2(event, _): return True uid2 = self._client.register(event2, cbk2) # assert for correct registration self.assertTrue(len(callbacks) == 2) self.assertTrue(callbacks[event2][uid2] == cbk2, 'Could not register event in local client.') def test_register_signal_replace(self): """ Make sure clients can replace already registered callbacks. """ event = catalog.CLIENT_UID d = defer.Deferred() def cbk_fail(event, _): return callFromThread(d.errback, event) def cbk_succeed(event, _): return callFromThread(d.callback, event) self._client.register(event, cbk_fail, uid=1) self._client.register(event, cbk_succeed, uid=1, replace=True) self._client.emit(event, None) return d def test_register_signal_replace_fails_when_replace_is_false(self): """ Make sure clients trying to replace already registered callbacks fail when replace=False """ event = catalog.CLIENT_UID self._client.register(event, lambda event, _: None, uid=1) self.assertRaises(CallbackAlreadyRegisteredError, self._client.register, event, lambda event, _: None, uid=1, replace=False) def test_register_more_than_one_callback_works(self): """ Make sure clients can replace already registered callbacks. """ event = catalog.CLIENT_UID d1 = defer.Deferred() def cbk1(event, _): return callFromThread(d1.callback, event) d2 = defer.Deferred() def cbk2(event, _): return d2.callback(event) self._client.register(event, cbk1) self._client.register(event, cbk2) self._client.emit(event, None) d = defer.gatherResults([d1, d2]) return d def test_client_receives_signal(self): """ Ensure clients can receive signals. """ event = catalog.CLIENT_UID d = defer.Deferred() def cbk(events, _): callFromThread(d.callback, event) self._client.register(event, cbk) self._client.emit(event, None) return d def test_client_unregister_all(self): """ Test that the client can unregister all events for one signal. """ event1 = catalog.CLIENT_UID d = defer.Deferred() # register more than one callback for the same event self._client.register(event1, lambda ev, _: callFromThread(d.errback, None)) self._client.register(event1, lambda ev, _: callFromThread(d.errback, None)) # unregister and emit the event self._client.unregister(event1) self._client.emit(event1, None) # register and emit another event so the deferred can succeed event2 = catalog.CLIENT_SESSION_ID self._client.register(event2, lambda ev, _: callFromThread(d.callback, None)) self._client.emit(event2, None) return d def test_client_unregister_by_uid(self): """ Test that the client can unregister an event by uid. """ event = catalog.CLIENT_UID d = defer.Deferred() # register one callback that would fail uid = self._client.register( event, lambda ev, _: callFromThread(d.errback, None)) # register one callback that will succeed self._client.register(event, lambda ev, _: callFromThread(d.callback, None)) # unregister by uid and emit the event self._client.unregister(event, uid=uid) self._client.emit(event, None) return d
class ImagesClient(object): """Handle the image/crops transfer from the airborne server.""" def __init__(self, app, ip_camera, role, ip_controller): if role == gs.CONTROLLER: self.images_folder = gs.CTRL_IMAGES_FOLDER self.crops_folder = gs.CTRL_CROPS_FOLDER self.flightdata_folder = gs.CTRL_FLIGHTDATA_FOLDER self.thumbs_folder = None else: self.images_folder = gs.IMAGES_FOLDER self.crops_folder = gs.CROPS_FOLDER self.flightdata_folder = gs.FLIGHTDATA_FOLDER self.thumbs_folder = gs.THUMBNAILS_FOLDER if not os.path.exists(self.images_folder): os.makedirs(self.images_folder) if not os.path.exists(self.crops_folder): os.makedirs(self.crops_folder) if not os.path.exists(self.flightdata_folder): os.makedirs(self.flightdata_folder) if self.thumbs_folder is not None and not os.path.exists( self.thumbs_folder): os.makedirs(self.thumbs_folder) self.role = role # # Setup the zmq socket used for receiving images. # self.zmq_factory = ZmqFactory() if self.role == gs.CONTROLLER: # # Socket for pulling notices about images from the camera. # endpoint = ZmqEndpoint( 'connect', 'tcp://{server_ip}:{zmq_port}'.format( server_ip=ip_camera, zmq_port=gs.ZMQ_CAMERA_NOTICE_PORT)) self.pull_socket = ZmqPullConnection(self.zmq_factory, endpoint) self.pull_socket.onPull = self.handlePullMessage # # Socket for requesting images from the camera. # endpoint = ZmqEndpoint( 'connect', 'tcp://{server_ip}:{zmq_port}'.format( server_ip=ip_camera, zmq_port=gs.ZMQ_CAMERA_FILES_PORT)) self.req_socket = ZmqREQConnection(self.zmq_factory, endpoint) # # Socket for publishing images to subscribers. # endpoint = ZmqEndpoint( 'bind', 'tcp://0.0.0.0:{zmq_port}'.format( zmq_port=gs.ZMQ_PRIMARY_GS_PORT)) self.pub_socket = MyZmqPubConnection(self.zmq_factory, endpoint) # # Socket for responding with queued images or sync data. # endpoint = ZmqEndpoint( 'bind', 'tcp://*:{zmq_port}'.format( zmq_port=gs.ZMQ_PRIMARY_GS_CONTROLLER_PORT)) self.sync_rep_socket = ControllerSyncConnection( self.zmq_factory, endpoint, self) else: # # Socket for subscribing to images. # endpoint = ZmqEndpoint( 'connect', 'tcp://{server_ip}:{zmq_port}'.format( server_ip=ip_controller, zmq_port=gs.ZMQ_PRIMARY_GS_PORT)) self.sub_socket = MyZmqSubConnection(self.zmq_factory, endpoint) self.sub_socket.subscribe(gs.SUB_TAG) self.sub_socket.gotMessage = self.handleNewMessage # # Socket for requesting queued images or sync data from the controller. # endpoint = ZmqEndpoint( 'connect', 'tcp://{server_ip}:{zmq_port}'.format( server_ip=ip_controller, zmq_port=gs.ZMQ_PRIMARY_GS_CONTROLLER_PORT)) self.sync_req_socket = ZmqREQConnection(self.zmq_factory, endpoint) log.msg('Sending sync request') self.requestSyncImageList() self.requestSyncCropList() self.app = app def shutdown(self): """Shutdown the zmq connection.""" self.zmq_factory.shutdown() def handlePullMessage(self, new_pull_message, ignore_tag=None): """Handle a notice from the camera about a new image.""" try: if self.role != gs.CONTROLLER: return data_type = new_pull_message[0] if data_type == gs.RESIZED_IMAGE: image_name = new_pull_message[1] log.msg("Got new pull message: {id}".format(id=image_name)) # # Request the new image. # self.req_socket.sendMsg(image_name).addCallback( self.handleNewMessage) else: # # Handle other messages normally. # self.handleNewMessage(new_pull_message, ignore_tag) except Exception as e: log.err(e, 'handlePullMessage') def handleNewMessage(self, new_data_message, ignore_tag=None): """Analyze the data received from the airborne server.""" try: if self.role == gs.CONTROLLER: self.pub_socket.publish(new_data_message, tag=gs.SUB_TAG) data_type = new_data_message[0] if data_type == gs.RESIZED_IMAGE: self.handleNewImg(new_data_message[1:]) elif data_type in (gs.MANUAL_CROP, gs.AUTO_CROP): self.handleNewCrop(data_type, new_data_message[1:]) elif data_type == gs.FLIGHT_DATA: self.handleNewFlightData(new_data_message[1:]) #log.msg("Got updated flight data") elif data_type == gs.AUTO_ANALYSIS: self.handleNewADLCresults(new_data_message[1:]) elif data_type == gs.STATE: self.handleState(new_data_message[1:]) elif data_type == gs.TARGET_INFO: self.handleNewTarget(new_data_message[1:]) else: log.msg( "Unkown data type received from image server: {data_type}". format(data_type=data_type)) except Exception as e: log.err(traceback.format_exc(), 'handleNewMessage') def handleNewImg(self, new_img_message): """Should be implemented by a subclass""" pass def handleNewCrop(self, crop_type, new_crop_message): """Should be implemented by a subclass""" pass def handleNewFlightData(self, new_fd_message): """Should be implemented by a subclass""" pass def handleNewTarget(self, new_target_message): """Should be implemented by a subclass""" pass def handleNewADLCresults(self, new_target_message): """Should be implemented by a subclass""" pass def handleSyncImageListRequest(self, message): """Should be implemented by a controller subclass""" """Should return response message""" pass def handleSyncCropListRequest(self, message): """Should be implemented by a controller subclass""" """Should return response message""" pass def handleSyncQueueRequest(self, message): """Should be implemented by a controller subclass""" """Should return response message""" pass def handleSyncImageRequest(self, message): """Should be implemented by a controller subclass""" """Should return response message""" pass def handleSyncCropRequest(self, message): """Should be implemented by a controller subclass""" """Should return response message""" pass def handleSyncTargetRequest(self, message): self.pub_socket.publish([gs.TARGET_INFO] + list(message), tag=gs.SUB_TAG) self.handleNewTarget(message) return [] def handleSyncRequest(self, message): cmd = message[0] rep = None if cmd == gs.SYNC_LIST: sync_type = message[1] if sync_type == gs.SYNC_LIST_IMAGES: rep = self.handleSyncImageListRequest() elif sync_type == gs.SYNC_LIST_CROPS: rep = self.handleSyncCropListRequest() else: log.msg( 'Unknown sync list request: {type}'.format(type=sync_type)) if rep is not None: rep = rep[:] rep.insert(0, sync_type) elif cmd == gs.SYNC_QUEUE: rep = self.handleSyncQueueRequest() elif cmd == gs.SYNC_IMAGE: rep = self.handleSyncImageRequest(message[1:]) elif cmd == gs.SYNC_CROP: rep = self.handleSyncCropRequest(message[1:]) elif cmd == gs.SYNC_TARGET: rep = self.handleSyncTargetRequest(message[1:]) if rep is not None: rep = rep[:] rep.insert(0, cmd) return rep def handleSyncImageListResponse(self, message): """Should be implemented by a primary/secondary subclass""" pass def handleSyncCropListResponse(self, message): """Should be implemented by a primary/secondary subclass""" pass def handleSyncImageResponse(self, message): """Should be implemented by a primary/secondary subclass""" pass def handleSyncCropResponse(self, message): """Should be implemented by a primary/secondary subclass""" pass def handleSyncQueueResponse(self, message): """Should be implemented by a primary/secondary subclass""" pass def handleSyncTargetResponse(self, message): pass def handleSyncResponse(self, message): if self.role == gs.CONTROLLER: return cmd = message[0] if cmd == gs.SYNC_LIST: if message[1] == gs.SYNC_LIST_IMAGES: self.handleSyncImageListResponse(message[2:]) elif message[1] == gs.SYNC_LIST_CROPS: self.handleSyncCropListResponse(message[2:]) else: log.msg('Unknown sync list response: {type}'.format( type=message[1])) elif cmd == gs.SYNC_QUEUE: self.handleSyncQueueResponse(message[1:]) elif cmd == gs.SYNC_IMAGE: self.handleSyncImageResponse(message[1:]) elif cmd == gs.SYNC_CROP: self.handleSyncCropResponse(message[1:]) elif cmd == gs.SYNC_TARGET: self.handleSyncTargetResponse(message[1:]) else: log.msg('Unknown sync response: {cmd}'.format(cmd=cmd)) def requestSyncMsg(self, *msg): if self.role != gs.CONTROLLER: self.sync_req_socket.sendMsg(*msg).addCallback( self.handleSyncResponse) def requestSyncImageList(self): self.requestSyncMsg(gs.SYNC_LIST, gs.SYNC_LIST_IMAGES) def requestSyncCropList(self): self.requestSyncMsg(gs.SYNC_LIST, gs.SYNC_LIST_CROPS) def requestSyncImage(self, image_name): self.requestSyncMsg(gs.SYNC_IMAGE, image_name) def requestSyncCrop(self, crop_name): self.requestSyncMsg(gs.SYNC_CROP, crop_name) def requestSyncQueue(self): self.requestSyncMsg(gs.SYNC_QUEUE) def notifyTargetManual(self, crop_name, crop_yaw, lat, lon, target_type, shape, shape_color, text, text_color, orientation_text, desc): self.requestSyncMsg(gs.SYNC_TARGET, gs.TARGET_MANUAL, crop_name, str(crop_yaw), target_type, str(lat), str(lon), shape, shape_color, text, text_color, orientation_text) def notifyTargetAuto(self, target_id, crop_name, crop_yaw, lat, lon, target_type, shape, shape_color, text, text_color, orientation_text): self.requestSyncMsg(gs.SYNC_TARGET, gs.TARGET_AUTO, target_id, crop_name, str(crop_yaw), target_type, str(lat), str(lon), shape, shape_color, text, text_color, orientation_text) def handleState(self, message): type = message[0] if type == gs.STATE_DOWNLOAD: self.handleDownloadState(message[1:]) elif type == gs.STATE_SHOOT: self.handleCameraState(message[1:]) def handleDownloadState(self, message): pass def handleCameraState(self, message): pass
class AutoServer(object): """Handle the communication of the automatic detection servers.""" def __init__(self, worker): log.msg("Initializing auto server") # # Setup the zmq socket used for receiving images. # self.zmq_factory = ZmqFactory() # # Socket for pulling images and crops from the automatic client. # endpoint = ZmqEndpoint( 'connect', 'tcp://localhost:{zmq_port}'.format( zmq_port=gs.ZMQ_AUTO_WORKER_PUSH)) self.pull_socket = ZmqPullConnection(self.zmq_factory, endpoint) self.pull_socket.onPull = self.handleNewMessage # # Socket for pushing results to the automatic client. # endpoint = ZmqEndpoint( 'connect', 'tcp://localhost:{zmq_port}'.format( zmq_port=gs.ZMQ_AUTO_WORKER_PULL)) self.push_socket = ZmqPushConnection(self.zmq_factory, endpoint) self.worker = worker def shutdown(self): """Shutdown the zmq connection.""" self.zmq_factory.shutdown() def requestCrops(self, img_path, crops): """Send a request from a worker to the auto client for crops.""" data = [gs.CROP_REQUESTS, img_path, json.dumps(crops)] try: log.msg("Sending crop requests for img {img}: {crops}.".format( img=img_path, crops=crops)) self.push_socket.push(data) except zmq.error.Again: log.msg("Failed sending crops request") def sendTarget(self, crop_path, target_details): """Send the details of a target (from a worker to the auto client).""" data = [gs.TARGET_DATA, crop_path, json.dumps(target_details)] try: log.msg("Sending of target in {crop}, with {data}.".format( crop=crop_path, data=data)) self.push_socket.push(data) except zmq.error.Again: log.msg( "Skipping sending of crop {crop}, no pull consumers...".format( crop=crop_path)) def handleNewMessage(self, new_data_message, ignore_tag=None): """Analyze the data received from the auto client.""" try: data_type = new_data_message[0] if data_type == gs.RESIZED_IMAGE: img_path, data_path = new_data_message[1:] crops = self.worker.processNewImage(img_path, data_path) self.requestCrops(img_path, crops) elif data_type == gs.AUTO_CROP: crop_path, yaw, lat, lon = new_data_message[1:] yaw = float(yaw) lat = float(lat) lon = float(lon) target_details = self.worker.processNewCrop( crop_path, yaw, lat, lon) self.sendTarget(crop_path, target_details) else: log.msg( "Unkown data type received from auto server: {data_type}". format(data_type=data_type)) except Exception as e: log.err(e, 'handleNewMessage')
class AutoClient(object): """Handle the communication with the automatic detection servers.""" def __init__(self, app): # # Setup the zmq socket used for receiving images. # self.zmq_factory = ZmqFactory() # # Socket for pushing images and crops to the automatic processing servers. # endpoint = ZmqEndpoint( 'bind', 'tcp://*:{zmq_port}'.format(zmq_port=gs.ZMQ_AUTO_WORKER_PUSH)) self.push_socket = ZmqPushConnection(self.zmq_factory, endpoint) # # Socket for pulling results from the automatic processing servers. # endpoint = ZmqEndpoint( 'bind', 'tcp://*:{zmq_port}'.format(zmq_port=gs.ZMQ_AUTO_WORKER_PULL)) self.pull_socket = ZmqPullConnection(self.zmq_factory, endpoint) self.pull_socket.onPull = self.handleNewMessage self.app = app def shutdown(self): """Shutdown the zmq connection.""" self.zmq_factory.shutdown() def sendNewImage(self, img_path, data_path): """Send a new image (got from the primary ip) to the auto server""" data = [gs.RESIZED_IMAGE, img_path, data_path] try: log.msg("Sending of img {img}.".format(img=img_path)) self.push_socket.push(data) except zmq.error.Again: log.msg("Skipping sending of {img}, no pull consumers...".format( img=img_path)) def sendNewCrop(self, crop_path, yaw, lat, lon): """Send a new (auto) crop from the primary ip to the auto server.""" data = [gs.AUTO_CROP, crop_path, yaw, lat, lon] try: log.msg("Sending of crop {crop}.".format(crop=crop_path)) self.push_socket.push(data) except zmq.error.Again: log.msg( "Skipping sending of crop {crop}, no pull consumers...".format( crop=crop_path)) def handleNewMessage(self, new_data_message, ignore_tag=None): """Analyze the data received from the auto server.""" try: data_type = new_data_message[0] if data_type == gs.CROP_REQUESTS: img_path, requested_crops = new_data_message[1], json.loads( new_data_message[2]) if len(requested_crops) == 0: log.msg('No target candidates found for image {img_name}'. format(img_name=os.path.split(img_path)[-1])) return self.app.downloadCrops(img_path, requested_crops) elif data_type == gs.TARGET_DATA: crop_path, target_details = new_data_message[1], json.loads( new_data_message[2]) if target_details is {}: log.msg('Crop {crop_name} not a target'.format( crop_name=os.path.split(crop_path)[-1])) return self.app.newTarget(crop_path, target_details) else: log.msg( "Unkown data type received from auto server: {data_type}". format(data_type=data_type)) except Exception as e: log.err(e, 'handleNewMessage')
class EventsGenericClientTestCase(object): def setUp(self): flags.set_events_enabled(True) self.factory = ZmqFactory() self._server = server.ensure_server( emit_addr="tcp://127.0.0.1:0", reg_addr="tcp://127.0.0.1:0", factory=self.factory, enable_curve=False) self._client.configure_client( emit_addr="tcp://127.0.0.1:%d" % self._server.pull_port, reg_addr="tcp://127.0.0.1:%d" % self._server.pub_port, factory=self.factory, enable_curve=False) def tearDown(self): flags.set_events_enabled(False) self.factory.shutdown() self._client.instance().reset() def test_client_register(self): """ Ensure clients can register callbacks. """ callbacks = self._client.instance().callbacks self.assertTrue(len(callbacks) == 0, 'There should be no callback for this event.') # register one event event1 = catalog.CLIENT_UID def cbk1(event, _): return True uid1 = self._client.register(event1, cbk1) # assert for correct registration self.assertTrue(len(callbacks) == 1) self.assertTrue(callbacks[event1][uid1] == cbk1, 'Could not register event in local client.') # register another event event2 = catalog.CLIENT_SESSION_ID def cbk2(event, _): return True uid2 = self._client.register(event2, cbk2) # assert for correct registration self.assertTrue(len(callbacks) == 2) self.assertTrue(callbacks[event2][uid2] == cbk2, 'Could not register event in local client.') def test_register_signal_replace(self): """ Make sure clients can replace already registered callbacks. """ event = catalog.CLIENT_UID d = defer.Deferred() def cbk_fail(event, _): return callFromThread(d.errback, event) def cbk_succeed(event, _): return callFromThread(d.callback, event) self._client.register(event, cbk_fail, uid=1) self._client.register(event, cbk_succeed, uid=1, replace=True) self._client.emit(event, None) return d def test_register_signal_replace_fails_when_replace_is_false(self): """ Make sure clients trying to replace already registered callbacks fail when replace=False """ event = catalog.CLIENT_UID self._client.register(event, lambda event, _: None, uid=1) self.assertRaises( CallbackAlreadyRegisteredError, self._client.register, event, lambda event, _: None, uid=1, replace=False) def test_register_more_than_one_callback_works(self): """ Make sure clients can replace already registered callbacks. """ event = catalog.CLIENT_UID d1 = defer.Deferred() def cbk1(event, _): return callFromThread(d1.callback, event) d2 = defer.Deferred() def cbk2(event, _): return d2.callback(event) self._client.register(event, cbk1) self._client.register(event, cbk2) self._client.emit(event, None) d = defer.gatherResults([d1, d2]) return d def test_client_receives_signal(self): """ Ensure clients can receive signals. """ event = catalog.CLIENT_UID d = defer.Deferred() def cbk(events, _): callFromThread(d.callback, event) self._client.register(event, cbk) self._client.emit(event, None) return d def test_client_unregister_all(self): """ Test that the client can unregister all events for one signal. """ event1 = catalog.CLIENT_UID d = defer.Deferred() # register more than one callback for the same event self._client.register( event1, lambda ev, _: callFromThread(d.errback, None)) self._client.register( event1, lambda ev, _: callFromThread(d.errback, None)) # unregister and emit the event self._client.unregister(event1) self._client.emit(event1, None) # register and emit another event so the deferred can succeed event2 = catalog.CLIENT_SESSION_ID self._client.register( event2, lambda ev, _: callFromThread(d.callback, None)) self._client.emit(event2, None) return d def test_client_unregister_by_uid(self): """ Test that the client can unregister an event by uid. """ event = catalog.CLIENT_UID d = defer.Deferred() # register one callback that would fail uid = self._client.register( event, lambda ev, _: callFromThread(d.errback, None)) # register one callback that will succeed self._client.register( event, lambda ev, _: callFromThread(d.callback, None)) # unregister by uid and emit the event self._client.unregister(event, uid=uid) self._client.emit(event, None) return d