def test_add_event(self):
     package = Package(Header(1, 2, "0" * 32))
     event1 = Event("TEST", 1, 2, 3)
     event2 = Event("FOO", "Bar")
     package.add_event(event1)
     package.add_event(event2)
     assert len(package.events) == 2
     assert event1 in package.events and event2 in package.events
     with pytest.raises(OverflowError):
         package.get_bytesize()
         package.add_event(Event("BIG", bytes(2030)))
 def test_dispatch_multiple_events(self):
     send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
     recv_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
     recv_socket.bind(("localhost", 0))
     connection = Connection(recv_socket.getsockname(), None)
     event = Event("TEST", 1, 2, 3, 4)
     another_event = Event("TEST", 3, 4, 5, 6)
     connection.dispatch_event(event)
     connection.dispatch_event(another_event)
     curio.run(connection._send_next_package, send_socket)
     data = curio.run(recv_socket.recv, Package._max_size)
     package = Package.from_datagram(data)
     assert package.events == [event, another_event]
Exemple #3
0
    def test_synchronous_event_handler(self):
        handler = UniversalEventHandler()
        testlist = []

        def on_foo(bar):
            testlist.append(bar)

        assert not handler.has_event_type("FOO")
        handler.register_event_handler("FOO", on_foo)
        assert handler.has_event_type("FOO")
        curio.run(handler.handle, Event("FOO", "baz"))
        assert "baz" in testlist
        handler.register_event_handler("BAR", lambda: testlist.pop())
        assert curio.run(handler.handle, Event("BAR")) == "baz"
        assert not testlist
Exemple #4
0
 def _read_out_event_block(event_block: bytes) -> list:
     events = []
     while event_block:
         bytesize = int.from_bytes(event_block[:2], "big")
         events.append(Event.from_bytes(event_block[2:bytesize + 2]))
         event_block = event_block[bytesize + 2:]
     return events
    def test_event_timeout_calbacks(self):
        async def sendto(*args):
            pass

        sock = type("socket", (), {"sendto": sendto})()

        class callback:
            count = 0

            def __init__(self):
                callback.count += 1

        with freeze_time("2012-01-14 12:00:01") as frozen_time:
            connection = Connection(("", 0), None)
            event = Event("TEST")
            connection.dispatch_event(event, timeout_callback=callback)
            curio.run(connection._send_next_package, sock)
            curio.run(connection._recv, Package(Header(1, 1, "0" * 32)))
            assert callback.count == 0
            frozen_time.tick()
            frozen_time.tick()
            curio.run(connection._recv, Package(Header(2, 1, "0" * 32)))
            assert callback.count == 0
            connection.dispatch_event(event, timeout_callback=callback)
            curio.run(connection._send_next_package, sock)
            frozen_time.tick()
            frozen_time.tick()
            curio.run(connection._recv, Package(Header(3, 1, "0" * 32)))
            assert callback.count == 1
    def test_event_ack_callbacks(self):
        async def sendto(*args):
            pass

        sock = type("socket", (), {"sendto": sendto})()

        class callback:
            count = 0

            def __init__(self):
                callback.count += 1

        connection = Connection(("", 0), None)
        event = Event("TEST")
        connection.dispatch_event(event, ack_callback=callback)
        curio.run(connection._send_next_package, sock)
        curio.run(connection._recv, Package(Header(1, 1, "0" * 32)))
        assert callback.count == 1
        curio.run(connection._recv, Package(Header(2, 1, "0" * 32)))
        assert callback.count == 1
        connection.dispatch_event(event, ack_callback=callback)
        connection.dispatch_event(event)
        connection.dispatch_event(event, ack_callback=callback)
        curio.run(connection._send_next_package, sock)
        assert connection.local_sequence == 2
        curio.run(connection._recv, Package(Header(3, 2, "1" + "0" * 31)))
        assert callback.count == 3
    def test_receive_events(self):
        class EventHandler:
            call_count = 0

            def __init__(self):
                self.events = []

            async def handle(self, event, **kwargs):
                EventHandler.call_count += 1
                self.events.append(event)

            def has_event_type(self, event_type):
                return True

        event_handler = EventHandler()
        connection = Connection(("", 0), event_handler)
        assert connection.event_handler == event_handler
        event = Event("TEST", 1, 2, 3, 4)
        package = Package(Header(1, 1, "1" * 32), [event, event])
        assert package.events == [event, event]
        assert connection.remote_sequence == 0
        curio.run(connection._recv, package)
        assert not connection._incoming_event_queue.empty()
        assert EventHandler.call_count == 0
        curio.run(connection._handle_next_event)
        assert EventHandler.call_count == 1
        assert not connection._incoming_event_queue.empty()
        assert event_handler.events == [event]
        curio.run(connection._handle_next_event)
        assert EventHandler.call_count == 2
        assert connection._incoming_event_queue.empty()
        assert event_handler.events == [event, event]
Exemple #8
0
    def test_asynchronous_event_handler_with_kwarg(self):
        handler = UniversalEventHandler()
        testlist = []

        async def on_foo(biz=0, bar="nobizbaz"):
            testlist.append(bar)

        handler.register_event_handler("FOO", on_foo)
        curio.run(handler.handle, Event("FOO", bar="bizbaz"))
        assert "bizbaz" in testlist
Exemple #9
0
    def dispatch_event(self,
                       event_type: str,
                       *args,
                       target_client="all",
                       retries: int = 0,
                       ack_callback=None,
                       **kwargs) -> None:
        """Send an event to one or all clients.

        # Arguments
        event_type (str): identifies the event and links it to a handler
        target_client (tuple, str): either `'all'` for an event broadcast, or a clients address as a tuple
        retries (int): number of times the event is to be resent in case it times out
        ack_callback (callable, coroutine): will be executed after the event was received
            and be passed a reference to the corresponding #pygase.connection.ServerConnection instance

        Additional positional and keyword arguments will be sent as event data and passed to the clients
        handler function.

        """
        event = Event(event_type, *args, **kwargs)

        def get_ack_callback(connection):
            if ack_callback is not None:
                return lambda: ack_callback(connection)
            return None

        timeout_callback = None
        if retries > 0:

            timeout_callback = lambda: self.dispatch_event(  # type: ignore
                event_type,
                *args,
                target_client=target_client,
                retries=retries - 1,
                ack_callback=ack_callback,
                **kwargs,
            ) or logger.warning(  # type: ignore
                f"Event of type {event_type} timed out. Retrying to send event to server."
            )

        if target_client == "all":
            for connection in self.connections.values():
                connection.dispatch_event(event, get_ack_callback(connection),
                                          timeout_callback, **kwargs)
        else:
            self.connections[target_client].dispatch_event(
                event, get_ack_callback(self.connections[target_client]),
                timeout_callback, **kwargs)
Exemple #10
0
    def add_event(self, event: Event) -> None:
        """Add a PyGaSe event to the package.

        # Arguments
        event (pygase.event.Event): the event to be attached to this package

        # Raises
        OverflowError: if the package has previously been converted to a datagram and
           and its size with the added event would exceed #Package._max_size (2048 bytex)

        """
        if self._datagram is not None:
            bytepack = event.to_bytes()
            if len(self._datagram) + len(bytepack) + 2 > self._max_size:
                raise OverflowError("Package exceeds the maximum size of " +
                                    str(self._max_size) + " bytes.")
            self._datagram += len(bytepack).to_bytes(2, "big") + bytepack
        self._events.append(event)
Exemple #11
0
    def dispatch_event(self, event_type: str, *args, retries: int = 0, ack_callback=None, **kwargs) -> None:
        """Send an event to the server.

        # Arguments
        event_type (str): event type identifier that links to a handler
        retries (int): number of times the event is to be resent in case it times out
        ack_callback (callable, coroutine): will be invoked after the event was received

        Additional positional and keyword arguments will be sent as event data and passed to the handler function.

        ---
        `ack_callback` should not perform any long-running blocking operations (say a `while True` loop), as that will
        block the connections asynchronous event loop. Use a coroutine instead, with appropriately placed `await`s.

        """
        event = Event(event_type, *args, **kwargs)
        timeout_callback = None
        if retries > 0:
            timeout_callback = lambda: self.dispatch_event(  # type: ignore
                event_type, *args, retries=retries - 1, ack_callback=ack_callback, **kwargs
            ) or logger.warning(  # type: ignore
                f"Event of type {event_type} timed out. Retrying to send event to server."
            )
        self.connection.dispatch_event(event, ack_callback, timeout_callback)
 def test_bytepacking(self):
     package = ServerPackage(Header(4, 5, "10" * 16), GameStateUpdate(2),
                             [Event("TEST", "Foo", "Bar")])
     datagram = package.to_datagram()
     unpacked_package = ServerPackage.from_datagram(datagram)
     assert package == unpacked_package
 def test_bytepacking(self):
     package = ClientPackage(Header(4, 5, "10" * 16), 1,
                             [Event("TEST", "Foo", "Bar")])
     datagram = package.to_datagram()
     unpacked_package = ClientPackage.from_datagram(datagram)
     assert package == unpacked_package
 def test_size_restriction(self):
     with pytest.raises(OverflowError) as error:
         Package(Header(1, 4, "0" * 32),
                 [Event("TEST", bytes(2048 - 13))]).to_datagram()
     assert str(
         error.value) == "Package exceeds the maximum size of 2048 bytes."
Exemple #15
0
 def test_bytepacking(self):
     event1 = Event("TEST", 1, 2, 3)
     event2 = Event.from_bytes(event1.to_bytes())
     assert event1 == event2