File: test_events.py

package info (click to toggle)
python-grpclib 0.4.9-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 676 kB
  • sloc: python: 6,864; makefile: 2
file content (150 lines) | stat: -rw-r--r-- 3,843 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
from unittest.mock import Mock, patch

import pytest

from grpclib.events import _Event, _EventMeta, _ident, listen
from grpclib.events import _Dispatch, _DispatchMeta, _dispatches


def _event_eq(self, other):
    if not isinstance(other, type(self)):
        return False
    for name in self.__slots__:
        if getattr(self, name) != getattr(other, name):
            return False
    return True


def patch_event():
    return patch.object(_Event, '__eq__', _event_eq)


def mock_callback(func):
    return Mock(side_effect=func)


def test_empty_event():
    class MyEvent(_Event, metaclass=_EventMeta):
        pass

    assert MyEvent.__slots__ == ()
    assert MyEvent.__readonly__ == frozenset()
    assert MyEvent.__payload__ == ()

    event = MyEvent()
    with pytest.raises(AttributeError, match='has no attribute'):
        event.foo = 5


def test_frozen_event():
    class MyEvent(_Event, metaclass=_EventMeta):
        a: int
        b: str

    assert set(MyEvent.__slots__) == {'a', 'b'}
    assert MyEvent.__readonly__ == frozenset(('a', 'b'))
    assert MyEvent.__payload__ == ()

    event = MyEvent(a=1, b='2')
    assert event.a == 1
    assert event.b == '2'
    with pytest.raises(AttributeError, match='^Read-only'):
        event.a = 5


def test_mixed_event():
    class MyEvent(_Event, metaclass=_EventMeta):
        __payload__ = ('a', 'b')
        a: int
        b: str
        c: int
        d: str

    assert set(MyEvent.__slots__) == {'a', 'b', 'c', 'd'}
    assert MyEvent.__readonly__ == frozenset(('c', 'd'))
    assert MyEvent.__payload__ == ('a', 'b')

    event = MyEvent(a=1, b='2', c=3, d='4')
    assert event.a == 1
    assert event.b == '2'
    assert event.c == 3
    assert event.d == '4'
    event.a = 5
    event.b = '6'
    assert event.a == 5
    assert event.b == '6'
    with pytest.raises(AttributeError, match='^Read-only'):
        event.c = 7
    with pytest.raises(AttributeError, match='^Read-only'):
        event.d = '8'


@pytest.mark.asyncio
async def test_dispatch():
    class MyEvent(_Event, metaclass=_EventMeta):
        __payload__ = ('a', 'b')
        a: int
        b: str
        c: int
        d: str

    class MyDispatch(_Dispatch, metaclass=_DispatchMeta):
        @_dispatches(MyEvent)
        async def my_event(self, a, b, *, c, d):
            return await self.__dispatch__(MyEvent(a=a, b=b, c=c, d=d))

    class Target:
        __dispatch__ = MyDispatch()

    assert Target.__dispatch__.my_event is _ident
    assert await Target.__dispatch__.my_event(1, 3, c=6, d=9) == (1, 3)

    @mock_callback
    async def callback(event: MyEvent):
        assert event.a == 2
        assert event.b == 4
        assert event.c == 8
        assert event.d == 16

    listen(Target, MyEvent, callback)

    assert Target.__dispatch__.my_event is not _ident
    assert await Target.__dispatch__.my_event(2, 4, c=8, d=16) == (2, 4)
    with patch_event():
        callback.assert_called_once_with(MyEvent(a=2, b=4, c=8, d=16))


@pytest.mark.asyncio
async def test_interrupt():
    class MyEvent(_Event, metaclass=_EventMeta):
        payload: int

    class MyDispatch(_Dispatch, metaclass=_DispatchMeta):
        @_dispatches(MyEvent)
        async def my_event(self, *, payload):
            return await self.__dispatch__(MyEvent(payload=payload))

    class Target:
        __dispatch__ = MyDispatch()

    @mock_callback
    async def cb1(_: MyEvent):
        pass

    @mock_callback
    async def cb2(event: MyEvent):
        event.interrupt()

    @mock_callback
    async def cb3(_: MyEvent):
        pass

    listen(Target, MyEvent, cb1)
    listen(Target, MyEvent, cb2)
    listen(Target, MyEvent, cb3)

    assert await Target.__dispatch__.my_event(payload=42) == ()

    cb1.assert_called_once()
    cb2.assert_called_once()
    assert not cb3.called