Exemple #1
0
    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()
Exemple #3
0
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()
Exemple #4
0
    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)
Exemple #6
0
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)
Exemple #7
0
def fixture_notifier(loop):
    return Notifier(loop=loop)
Exemple #8
0
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)}
Exemple #9
0
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, '{}')}
Exemple #10
0
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, '{}')]
Exemple #11
0
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: {}}
Exemple #12
0
    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)