-
Notifications
You must be signed in to change notification settings - Fork 123
Expand file tree
/
Copy pathtest_hap_server.py
More file actions
198 lines (167 loc) · 6.49 KB
/
test_hap_server.py
File metadata and controls
198 lines (167 loc) · 6.49 KB
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
"""Tests for the HAPServer."""
import asyncio
from unittest.mock import MagicMock, patch
import pytest
from pyhap import hap_server
from pyhap.accessory import Accessory
from pyhap.accessory_driver import AccessoryDriver
from pyhap.hap_protocol import HAPServerProtocol
@pytest.mark.asyncio
async def test_we_can_start_stop(driver):
"""Test we can start and stop."""
loop = asyncio.get_event_loop()
addr_info = ("0.0.0.0", None)
client_1_addr_info = ("1.2.3.4", 44433)
client_2_addr_info = ("4.5.6.7", 33444)
server = hap_server.HAPServer(addr_info, driver)
await server.async_start(loop)
server.connections[client_1_addr_info] = MagicMock()
server.connections[client_2_addr_info] = MagicMock()
server.async_stop()
@pytest.mark.asyncio
async def test_we_can_connect():
"""Test we can start, connect, and stop."""
loop = asyncio.get_event_loop()
with patch("pyhap.accessory_driver.AsyncZeroconf"), patch(
"pyhap.accessory_driver.AccessoryDriver.persist"
):
driver = AccessoryDriver(loop=loop)
driver.add_accessory(Accessory(driver, "TestAcc"))
addr_info = ("0.0.0.0", None)
server = hap_server.HAPServer(addr_info, driver)
await server.async_start(loop)
sock = server.server.sockets[0]
assert not server.connections
_, port = sock.getsockname()
_, writer = await asyncio.open_connection("127.0.0.1", port)
# flush out any call_soon
for _ in range(3):
await asyncio.sleep(0)
assert server.connections
server.async_stop()
writer.close()
@pytest.mark.asyncio
async def test_idle_connection_cleanup():
"""Test we cleanup idle connections."""
loop = asyncio.get_event_loop()
addr_info = ("0.0.0.0", None)
client_1_addr_info = ("1.2.3.4", 44433)
with patch.object(hap_server, "IDLE_CONNECTION_CHECK_INTERVAL_SECONDS", 0), patch(
"pyhap.accessory_driver.AsyncZeroconf"
), patch("pyhap.accessory_driver.AccessoryDriver.persist"), patch(
"pyhap.accessory_driver.AccessoryDriver.load"
):
driver = AccessoryDriver(loop=loop)
server = hap_server.HAPServer(addr_info, driver)
await server.async_start(loop)
check_idle = MagicMock()
server.connections[client_1_addr_info] = MagicMock(check_idle=check_idle)
for _ in range(3):
await asyncio.sleep(0)
assert check_idle.called
check_idle.reset_mock()
for _ in range(3):
await asyncio.sleep(0)
assert check_idle.called
server.async_stop()
@pytest.mark.asyncio
async def test_push_event(driver):
"""Test we can create and send an event."""
addr_info = ("1.2.3.4", 1234)
server = hap_server.HAPServer(("127.0.01", 5555), driver)
server.loop = asyncio.get_event_loop()
hap_events = []
def _save_event(hap_event):
hap_events.append(hap_event)
hap_server_protocol = HAPServerProtocol(
server.loop, server.connections, server.accessory_handler
)
hap_server_protocol.write = _save_event
hap_server_protocol.peername = addr_info
server.accessory_handler.topics["1.33"] = {addr_info}
server.accessory_handler.topics["2.33"] = {addr_info}
server.accessory_handler.topics["3.33"] = {addr_info}
assert server.push_event({"aid": 1, "iid": 33, "value": False}, addr_info) is False
await asyncio.sleep(0)
server.connections[addr_info] = hap_server_protocol
assert (
server.push_event({"aid": 1, "iid": 33, "value": False}, addr_info, True)
is True
)
assert (
server.push_event({"aid": 2, "iid": 33, "value": False}, addr_info, True)
is True
)
assert (
server.push_event({"aid": 3, "iid": 33, "value": False}, addr_info, True)
is True
)
await asyncio.sleep(0)
assert hap_events == [
b"EVENT/1.0 200 OK\r\nContent-Type: application/hap+json\r\nContent-Length: 120\r\n\r\n"
b'{"characteristics":[{"aid":1,"iid":33,"value":false},'
b'{"aid":2,"iid":33,"value":false},{"aid":3,"iid":33,"value":false}]}'
]
hap_events = []
assert (
server.push_event({"aid": 1, "iid": 33, "value": False}, addr_info, False)
is True
)
assert (
server.push_event({"aid": 2, "iid": 33, "value": False}, addr_info, False)
is True
)
assert (
server.push_event({"aid": 3, "iid": 33, "value": False}, addr_info, False)
is True
)
await asyncio.sleep(0)
assert not hap_events
# Ensure that a the event is not sent if its unsubscribed during
# the coalesce delay
server.accessory_handler.topics["1.33"].remove(addr_info)
await asyncio.sleep(0.55)
assert hap_events == [
b"EVENT/1.0 200 OK\r\nContent-Type: application/hap+json\r\nContent-Length: 87\r\n\r\n"
b'{"characteristics":[{"aid":2,"iid":33,"value":false},{"aid":3,"iid":33,"value":false}]}'
]
@pytest.mark.asyncio
async def test_push_event_overwrites_old_pending_events(driver):
"""Test push event overwrites old events in the event queue.
iOS 15 had a breaking change where events are no longer processed
in order. We want to make sure when we send an event message we
only send the latest state and overwrite all the previous states
for the same AID/IID that are in the queue when the state changes
before the event is sent.
"""
addr_info = ("1.2.3.4", 1234)
server = hap_server.HAPServer(("127.0.01", 5555), driver)
server.loop = asyncio.get_event_loop()
hap_events = []
def _save_event(hap_event):
hap_events.append(hap_event)
hap_server_protocol = HAPServerProtocol(
server.loop, server.connections, server.accessory_handler
)
hap_server_protocol.write = _save_event
hap_server_protocol.peername = addr_info
server.accessory_handler.topics["1.33"] = {addr_info}
server.accessory_handler.topics["2.33"] = {addr_info}
server.connections[addr_info] = hap_server_protocol
assert (
server.push_event({"aid": 1, "iid": 33, "value": False}, addr_info, True)
is True
)
assert (
server.push_event({"aid": 1, "iid": 33, "value": True}, addr_info, True) is True
)
assert (
server.push_event({"aid": 2, "iid": 33, "value": False}, addr_info, True)
is True
)
await asyncio.sleep(0)
assert hap_events == [
b"EVENT/1.0 200 OK\r\nContent-Type: application/hap+json\r\nContent-Length: 86\r\n\r\n"
b'{"characteristics":[{"aid":1,"iid":33,"value":true},'
b'{"aid":2,"iid":33,"value":false}]}'
]