|
3 | 3 |
|
4 | 4 | """ Unit tests for zeroconf._services.browser. """ |
5 | 5 |
|
6 | | -import asyncio |
7 | 6 | import logging |
8 | 7 | import socket |
9 | 8 | import time |
|
17 | 16 | import zeroconf as r |
18 | 17 | from zeroconf import DNSPointer, DNSQuestion, const, current_time_millis, millis_to_seconds |
19 | 18 | import zeroconf._services.browser as _services_browser |
20 | | -from zeroconf import Zeroconf |
| 19 | +from zeroconf import _core, _handlers, Zeroconf |
21 | 20 | from zeroconf._services import ServiceStateChange |
22 | 21 | from zeroconf._services.browser import ServiceBrowser |
23 | 22 | from zeroconf._services.info import ServiceInfo |
@@ -1100,3 +1099,73 @@ def mock_incoming_msg(records) -> r.DNSIncoming: |
1100 | 1099 | browser.cancel() |
1101 | 1100 |
|
1102 | 1101 | zc.close() |
| 1102 | + |
| 1103 | + |
| 1104 | +@patch.object(_handlers, '_DNS_PTR_MIN_TTL', 1) |
| 1105 | +@patch.object(_core, "_CACHE_CLEANUP_INTERVAL", 10) |
| 1106 | +def test_service_browser_expire_callbacks(): |
| 1107 | + """Test that the ServiceBrowser matching does not match partial names.""" |
| 1108 | + # instantiate a zeroconf instance |
| 1109 | + zc = Zeroconf(interfaces=['127.0.0.1']) |
| 1110 | + # start a browser |
| 1111 | + type_ = "_http._tcp.local." |
| 1112 | + registration_name = "xxxyyy.%s" % type_ |
| 1113 | + callbacks = [] |
| 1114 | + |
| 1115 | + class MyServiceListener(r.ServiceListener): |
| 1116 | + def add_service(self, zc, type_, name) -> None: |
| 1117 | + nonlocal callbacks |
| 1118 | + if name == registration_name: |
| 1119 | + callbacks.append(("add", type_, name)) |
| 1120 | + |
| 1121 | + def remove_service(self, zc, type_, name) -> None: |
| 1122 | + nonlocal callbacks |
| 1123 | + if name == registration_name: |
| 1124 | + callbacks.append(("remove", type_, name)) |
| 1125 | + |
| 1126 | + def update_service(self, zc, type_, name) -> None: |
| 1127 | + nonlocal callbacks |
| 1128 | + if name == registration_name: |
| 1129 | + callbacks.append(("update", type_, name)) |
| 1130 | + |
| 1131 | + listener = MyServiceListener() |
| 1132 | + |
| 1133 | + browser = r.ServiceBrowser(zc, type_, None, listener) |
| 1134 | + |
| 1135 | + desc = {'path': '/~paulsm/'} |
| 1136 | + address_parsed = "10.0.1.2" |
| 1137 | + address = socket.inet_aton(address_parsed) |
| 1138 | + info = ServiceInfo( |
| 1139 | + type_, registration_name, 80, 0, 0, desc, "ash-2.local.", host_ttl=1, other_ttl=1, addresses=[address] |
| 1140 | + ) |
| 1141 | + |
| 1142 | + def mock_incoming_msg(records) -> r.DNSIncoming: |
| 1143 | + generated = r.DNSOutgoing(const._FLAGS_QR_RESPONSE) |
| 1144 | + for record in records: |
| 1145 | + generated.add_answer_at_time(record, 0) |
| 1146 | + return r.DNSIncoming(generated.packets()[0]) |
| 1147 | + |
| 1148 | + _inject_response( |
| 1149 | + zc, |
| 1150 | + mock_incoming_msg([info.dns_pointer(), info.dns_service(), info.dns_text(), *info.dns_addresses()]), |
| 1151 | + ) |
| 1152 | + time.sleep(0.2) |
| 1153 | + info.port = 400 |
| 1154 | + _inject_response( |
| 1155 | + zc, |
| 1156 | + mock_incoming_msg([info.dns_service()]), |
| 1157 | + ) |
| 1158 | + |
| 1159 | + assert callbacks == [ |
| 1160 | + ('add', type_, registration_name), |
| 1161 | + ('update', type_, registration_name), |
| 1162 | + ] |
| 1163 | + time.sleep(1.2) |
| 1164 | + assert callbacks == [ |
| 1165 | + ('add', type_, registration_name), |
| 1166 | + ('update', type_, registration_name), |
| 1167 | + ('remove', type_, registration_name), |
| 1168 | + ] |
| 1169 | + browser.cancel() |
| 1170 | + |
| 1171 | + zc.close() |
0 commit comments