async def test_gigachannel_search(self): """ Test searching several nodes for metadata entries based on title text """ # We do not want the query back mechanism and introduction callback to interfere with this test for node in self.nodes: node.overlay.rqc_settings.max_channel_query_back = 0 await self.introduce_nodes() U_CHANNEL = "ubuntu channel" U_TORRENT = "ubuntu torrent" # Add test metadata to node 0 with db_session: self.nodes[0].overlay.mds.ChannelMetadata.create_channel( U_CHANNEL, "") self.nodes[0].overlay.mds.ChannelMetadata.create_channel( "debian channel", "") # Add test metadata to node 1 with db_session: self.nodes[1].overlay.mds.TorrentMetadata( title=U_TORRENT, infohash=random_infohash()) self.nodes[1].overlay.mds.TorrentMetadata( title="debian torrent", infohash=random_infohash()) notifier = Notifier(loop=self.loop) notifier.notify = Mock() self.nodes[2].overlay.notifier = notifier self.nodes[2].overlay.send_search_request(**{"txt_filter": "ubuntu*"}) await self.deliver_messages(timeout=0.5) # Check that the notifier callback was called on both entries titles = sorted(call.args[1]["results"][0]["name"] for call in notifier.notify.call_args_list) assert titles == [U_CHANNEL, U_TORRENT] with db_session: assert self.nodes[2].overlay.mds.ChannelNode.select().count() == 2 assert (self.nodes[2].overlay.mds.ChannelNode.select( lambda g: g.title in (U_CHANNEL, U_TORRENT)).count() == 2)
async def fixture_resource_monitor(tmp_path): config = ResourceMonitorSettings() notifier = Notifier() resource_monitor = CoreResourceMonitor(state_dir=tmp_path, log_dir=tmp_path, config=config, notifier=notifier, history_size=10) yield resource_monitor await resource_monitor.stop()
def test_notify_call_soon_threadsafe_with_exception(loop): notifier = Notifier(loop=loop) notifier.logger = MagicMock() notifier.loop = MagicMock(call_soon_threadsafe=MagicMock( side_effect=RuntimeError)) def topic1(x: int): pass def observer1(x: int): pass notifier.add_observer(topic1, observer1) notifier[topic1](123) notifier.logger.warning.assert_called_once()
def __init__(self, config: TriblerConfig = None, components: List[Component] = (), shutdown_event: Event = None, notifier: Notifier = None, failfast: bool = True): # deepcode ignore unguarded~next~call: not necessary to catch StopIteration on infinite iterator self.id = next(Session._next_session_id) self.failfast = failfast self.logger = logging.getLogger(self.__class__.__name__) self.config: TriblerConfig = config or TriblerConfig() self.shutdown_event: Event = shutdown_event or Event() self.notifier: Notifier = notifier or Notifier() self.components: Dict[Type[Component], Component] = {} for component in components: self.register(component.__class__, component) # Reserve various (possibly) fixed ports to prevent # components from occupying those accidentally reserve_ports([ config.libtorrent.port, config.api.http_port, config.api.https_port, config.ipv8.port ])
def __init__(self, api_port, api_key, error_handler): QNetworkAccessManager.__init__(self) url = QUrl("http://localhost:%d/events" % api_port) self.request = QNetworkRequest(url) self.request.setRawHeader(b'X-Api-Key', api_key.encode('ascii')) self.remaining_connection_attempts = CORE_CONNECTION_ATTEMPTS_LIMIT self.connect_timer = QTimer() self.current_event_string = "" self.reply = None self.shutting_down = False self.error_handler = error_handler self._logger = logging.getLogger(self.__class__.__name__) # This flag is used to prevent race condition when starting GUI tests self.tribler_started_flag = False self.connect_timer.setSingleShot(True) connect(self.connect_timer.timeout, self.connect) self.notifier = notifier = Notifier() notifier.add_observer(notifications.events_start, self.on_events_start) notifier.add_observer(notifications.tribler_exception, self.on_tribler_exception) notifier.add_observer(notifications.channel_entity_updated, self.on_channel_entity_updated) notifier.add_observer(notifications.tribler_new_version, self.on_tribler_new_version) notifier.add_observer(notifications.channel_discovered, self.on_channel_discovered) notifier.add_observer(notifications.torrent_finished, self.on_torrent_finished) notifier.add_observer(notifications.low_space, self.on_low_space) notifier.add_observer(notifications.remote_query_results, self.on_remote_query_results) notifier.add_observer(notifications.tribler_shutdown_state, self.on_tribler_shutdown_state) notifier.add_observer(notifications.report_config_error, self.on_report_config_error)
def test_two_topics_with_the_same_name(): notifier = Notifier() def topic1(x: int): pass def observer1(x: int): pass notifier.add_observer(topic1, observer1) def topic1(x: int): # pylint: disable=function-redefined # try to define another topic with the same name pass def observer2(x: int): pass with pytest.raises( NotifierError, match= '^Cannot register topic <.*topic1.*> because topic name topic1 ' 'is already taken by another topic <.*topic1.*>$'): notifier.add_observer(topic1, observer2)
def fixture_notifier(loop): return Notifier(loop=loop)
async def test_notify_with_exception(loop): # test that notify works as expected even if one of callbacks will raise an exception def topic(x: int): pass calls = [] def observer1(x: int): calls.append(('observer1', x)) def observer2(x: int): calls.append(('observer2', x)) raise ZeroDivisionError def observer3(x: int): calls.append(('observer3', x)) notifier = Notifier( ) # First, let's create a notifier without a loop specified notifier.add_observer(topic, observer1) notifier.add_observer(topic, observer2) notifier.add_observer(topic, observer3) notifier[topic](123) # when notifier is created without specifying a loop, it processes notifications synchronously assert calls == [('observer1', 123), ('observer2', 123), ('observer3', 123)] calls.clear() notifier = Notifier( loop=loop) # Now, let's create a notifier tied to a loop notifier.add_observer(topic, observer1) notifier.add_observer(topic, observer2) notifier.add_observer(topic, observer3) notifier[topic](123) # when notifier tied to a loop, it processes notifications asynchronously assert calls == [] await asyncio.sleep(0.1) # now notifications should be processed assert set(calls) == {('observer1', 123), ('observer2', 123), ('observer3', 123)}
async def test_notify_async(loop): def topic_a(a: int, b: str): pass def topic_b(x: int): pass calls = [] def observer_a1(a: int, b: str): calls.append(('a1', a, b)) def observer_a2(a: int, b: str): calls.append(('a2', a, b)) def observer_b1(x: int): calls.append(('b1', x)) def generic_1(*args, **kwargs): calls.append((('generic1', ) + args + (repr(kwargs), ))) def generic_2(*args, **kwargs): calls.append((('generic2', ) + args + (repr(kwargs), ))) notifier = Notifier(loop=loop) notifier.add_observer(topic_a, observer_a1) # add an observer notifier.add_observer( topic_a, observer_a1 ) # adding the same observer multiple times should affect nothing notifier.add_generic_observer(generic_1) # add a generic observer # An attempt to add the same observer with different `synchronous` option value should raise an error with pytest.raises(NotifierError, match=r'^Cannot register the same observer ' r'with a different value of `synchronous` option$'): notifier.add_observer(topic_a, observer_a1, synchronous=True) with pytest.raises(TypeError): notifier[topic_a](123) assert calls == [] notifier[topic_a](1, 'aaa') await asyncio.sleep(0.1) assert set(calls) == {('generic1', topic_a, 1, 'aaa', '{}'), ('a1', 1, 'aaa')} calls.clear() notifier.add_observer( topic_a, observer_a2) # add a second observer to the same topic notifier.add_observer(topic_b, observer_b1) # observer to a different topic notifier.add_generic_observer(generic_2) # a second generic observer notifier[topic_a](2, 'bbb') await asyncio.sleep(0.1) assert set(calls) == {('generic1', topic_a, 2, 'bbb', '{}'), ('generic2', topic_a, 2, 'bbb', '{}'), ('a1', 2, 'bbb'), ('a2', 2, 'bbb')} calls.clear() notifier[topic_b](x=111) await asyncio.sleep(0.1) assert set(calls) == {('generic1', topic_b, "{'x': 111}"), ('generic2', topic_b, "{'x': 111}"), ('b1', 111)} calls.clear() notifier.remove_observer(topic_b, observer_b1) notifier.remove_generic_observer(generic_1) notifier[topic_b](222) await asyncio.sleep(0.1) assert set(calls) == {('generic2', topic_b, 222, '{}')}
def test_notify(): def topic_a(a: int, b: str): pass def topic_b(x: int): pass calls = [] def observer_a1(a: int, b: str): calls.append(('a1', a, b)) def observer_a2(a: int, b: str): calls.append(('a2', a, b)) def observer_b1(x: int): calls.append(('b1', x)) def generic_1(*args, **kwargs): calls.append((('generic1', ) + args + (repr(kwargs), ))) def generic_2(*args, **kwargs): calls.append((('generic2', ) + args + (repr(kwargs), ))) notifier = Notifier() notifier.add_observer(topic_a, observer_a1) # add an observer notifier.add_observer( topic_a, observer_a1 ) # adding the same observer multiple times should affect nothing notifier.add_generic_observer(generic_1) # add a generic observer with pytest.raises(TypeError): notifier[topic_a](123) assert calls == [] notifier[topic_a](1, 'aaa') assert calls == [('generic1', topic_a, 1, 'aaa', '{}'), ('a1', 1, 'aaa')] calls.clear() notifier.add_observer( topic_a, observer_a2) # add a second observer to the same topic notifier.add_observer(topic_b, observer_b1) # observer to a different topic notifier.add_generic_observer(generic_2) # a second generic observer notifier[topic_a](2, 'bbb') assert calls == [('generic1', topic_a, 2, 'bbb', '{}'), ('generic2', topic_a, 2, 'bbb', '{}'), ('a1', 2, 'bbb'), ('a2', 2, 'bbb')] calls.clear() notifier[topic_b](x=111) assert calls == [('generic1', topic_b, "{'x': 111}"), ('generic2', topic_b, "{'x': 111}"), ('b1', 111)] calls.clear() notifier.logger.warning = MagicMock() notifier.notify_by_topic_name('non_existent_topic', x=1, y=2) notifier.logger.warning.assert_called_once_with( 'Topic with name `non_existent_topic` not found') notifier.notify_by_topic_name('topic_b', x=111) assert calls == [('generic1', topic_b, "{'x': 111}"), ('generic2', topic_b, "{'x': 111}"), ('b1', 111)] calls.clear() notifier.remove_observer(topic_b, observer_b1) notifier.remove_generic_observer(generic_1) notifier[topic_b](222) assert calls == [('generic2', topic_b, 222, '{}')]
def test_add_remove_observer(): notifier = Notifier() # A topic should be callable with pytest.raises(TypeError, match=r"^'topic' is not a callable object$"): notifier.add_observer('topic', lambda x: x) def topic1(x: int): pass # An observer should be callable as well with pytest.raises(TypeError, match=r"^'observer' is not a callable object$"): notifier.add_observer(topic1, "observer") def observer1(): pass # Topic and observer functions should have the same number of arguments with pytest.raises( TypeError, match=r'^Cannot add observer <function .*> to topic "topic1": ' r'the callback signature \(\) does not match the topic signature \(x: int\)$' ): notifier.add_observer(topic1, observer1) def observer2(x): pass # Topic and observer functions should have the same argument types with pytest.raises( TypeError, match=r'^Cannot add observer <function .*> to topic "topic1": ' r'the callback signature \(x\) does not match the topic signature \(x: int\)$' ): notifier.add_observer(topic1, observer2) def observer3(x: str): pass # Topic and observer functions should have the same argument types with pytest.raises( TypeError, match=r'^Cannot add observer <function .*> to topic "topic1": ' r'the callback signature \(x: str\) ' r'does not match the topic signature \(x: int\)$'): notifier.add_observer(topic1, observer3) def observer4(y: int): pass # Topic and observer functions should have the same argument names with pytest.raises( TypeError, match=r'^Cannot add observer <function .*> to topic "topic1": ' r'the callback signature \(y: int\) ' r'does not match the topic signature \(x: int\)$'): notifier.add_observer(topic1, observer4) def observer5(x: int, y: int): pass # Topic and observer functions should have the same number of arguments with pytest.raises( TypeError, match=r'^Cannot add observer <function .*> to topic "topic1": ' r'the callback signature \(x: int, y: int\) ' r'does not match the topic signature \(x: int\)$'): notifier.add_observer(topic1, observer5) def observer6(x: int = None): pass # Topic and observer functions should have the same argument defaults with pytest.raises( TypeError, match=r'^Cannot add observer <function .*> to topic "topic1": ' r'the callback signature \(x: int = None\) ' r'does not match the topic signature \(x: int\)$'): notifier.add_observer(topic1, observer6) async def async1(x: int): pass # Topic and observer cannot be async functions with pytest.raises( TypeError, match=r'^Topic cannot be a coroutine function. Got: <function .*>$' ): notifier.add_observer(async1, topic1) with pytest.raises( TypeError, match= r'^Observer cannot be a coroutine function. Got: <function .*>$'): notifier.add_observer(topic1, async1) with pytest.raises( TypeError, match= r'^Topic and observer cannot be the same function. Got: <function .*>$' ): notifier.add_observer(topic1, topic1) def observer7(x: int): pass with pytest.raises( TypeError, match=r"^`synchronous` option may be True, False or None. Got: 1$" ): notifier.add_observer(topic1, observer7, synchronous=1) with pytest.raises(TypeError, match=r"^synchronous=False option cannot be specified " r"for a notifier without an event loop$"): notifier.add_observer(topic1, observer7, synchronous=False) assert not notifier.topics_by_name assert not notifier.topics assert not notifier.generic_observers assert not notifier.interceptors # add first observer to topic1 notifier.add_observer(topic1, observer7) assert notifier.topics_by_name == {'topic1': topic1} assert notifier.topics == {topic1: {observer7: True}} # adding the same observer the second time should change nothing notifier.add_observer(topic1, observer7) assert notifier.topics == {topic1: {observer7: True}} def observer8(x: int): pass # add second observer to topic1 notifier.add_observer(topic1, observer8) assert notifier.topics == {topic1: {observer7: True, observer8: True}} # generic observers and interceptors were not added assert not notifier.generic_observers assert not notifier.interceptors def topic2(x: int): pass def observer9(x: int): pass # no exception when removing an observer from non-registered topic notifier.remove_observer(topic2, observer7) # no exception when removing a non-registered observer notifier.remove_observer(topic1, observer9) # we still has two observers for topic1 topic assert notifier.topics == { topic1: { observer7: True, observer8: True }, topic2: {} } # remove the first observer from the topic1 topic notifier.remove_observer(topic1, observer7) assert notifier.topics == {topic1: {observer8: True}, topic2: {}} # remove last observer from the topic1 topic notifier.remove_observer(topic1, observer8) assert notifier.topics == {topic1: {}, topic2: {}}
async def test_remote_select_subscribed_channels(self): """ Test querying remote peers for subscribed channels and updating local votes accordingly. """ # We do not want the query back mechanism to interfere with this test self.nodes[1].overlay.rqc_settings.max_channel_query_back = 0 num_channels = 5 with db_session: # Create one channel with zero contents, to check that only non-empty channels are served self.nodes[0].overlay.mds.ChannelMetadata.create_channel( "channel sub", "") # Create one channel that has not yet been processed (with local_version<timestamp) incomplete_chan = self.nodes[ 0].overlay.mds.ChannelMetadata.create_channel( "channel sub", "") incomplete_chan.num_entries = 10 incomplete_chan.sign() for _ in range(0, num_channels): chan = self.nodes[ 0].overlay.mds.ChannelMetadata.create_channel( "channel sub", "") chan.local_version = chan.timestamp chan.num_entries = 10 chan.sign() for _ in range(0, num_channels): channel_uns = self.nodes[ 0].overlay.mds.ChannelMetadata.create_channel( "channel unsub", "") channel_uns.subscribed = False notifier = Notifier(loop=self.loop) notifier.notify = Mock() self.nodes[1].overlay.notifier = notifier peer = self.nodes[0].my_peer await self.introduce_nodes() await self.deliver_messages(timeout=0.5) # Check that the notifier callback is called on new channel entries notifier.notify.assert_called() assert "results" in notifier.notify.call_args.args[1] with db_session: received_channels = self.nodes[ 1].overlay.mds.ChannelMetadata.select( lambda g: g.title == "channel sub") self.assertEqual(num_channels, received_channels.count()) # Only subscribed channels should have been transported received_channels_all = self.nodes[ 1].overlay.mds.ChannelMetadata.select() self.assertEqual(num_channels, received_channels_all.count()) # Make sure the subscribed channels transport counted as voting self.assertEqual( self.nodes[1].overlay.mds.ChannelPeer.select().first(). public_key, peer.public_key.key_to_bin()[10:]) for chan in self.nodes[1].overlay.mds.ChannelMetadata.select(): self.assertTrue(chan.votes > 0.0)