|
15 | 15 | import pytest |
16 | 16 |
|
17 | 17 | from zeroconf.aio import AsyncServiceBrowser, AsyncServiceInfo, AsyncZeroconf, AsyncZeroconfServiceTypes |
18 | | -from zeroconf import DNSIncoming, ServiceStateChange, Zeroconf, const |
| 18 | +from zeroconf import ( |
| 19 | + DNSIncoming, |
| 20 | + DNSOutgoing, |
| 21 | + DNSPointer, |
| 22 | + DNSService, |
| 23 | + DNSAddress, |
| 24 | + ServiceStateChange, |
| 25 | + Zeroconf, |
| 26 | + const, |
| 27 | +) |
19 | 28 | from zeroconf.const import _LISTENER_TIME |
20 | 29 | from zeroconf._exceptions import BadTypeInNameException, NonUniqueNameException, ServiceNameAlreadyRegistered |
21 | 30 | from zeroconf._services import ServiceListener |
@@ -823,3 +832,84 @@ def send(out, addr=const._MDNS_ADDR, port=const._MDNS_PORT): |
823 | 832 | assert second_outgoing.questions[0].unicast == False |
824 | 833 | finally: |
825 | 834 | await aiozc.async_close() |
| 835 | + |
| 836 | + |
| 837 | +@pytest.mark.asyncio |
| 838 | +async def test_service_browser_ignores_unrelated_updates(): |
| 839 | + """Test that the ServiceBrowser ignores unrelated updates.""" |
| 840 | + |
| 841 | + # instantiate a zeroconf instance |
| 842 | + aiozc = AsyncZeroconf(interfaces=['127.0.0.1']) |
| 843 | + zc = aiozc.zeroconf |
| 844 | + type_ = "_veryuniqueone._tcp.local." |
| 845 | + registration_name = "xxxyyy.%s" % type_ |
| 846 | + callbacks = [] |
| 847 | + |
| 848 | + class MyServiceListener(ServiceListener): |
| 849 | + def add_service(self, zc, type_, name) -> None: |
| 850 | + nonlocal callbacks |
| 851 | + if name == registration_name: |
| 852 | + callbacks.append(("add", type_, name)) |
| 853 | + |
| 854 | + def remove_service(self, zc, type_, name) -> None: |
| 855 | + nonlocal callbacks |
| 856 | + if name == registration_name: |
| 857 | + callbacks.append(("remove", type_, name)) |
| 858 | + |
| 859 | + def update_service(self, zc, type_, name) -> None: |
| 860 | + nonlocal callbacks |
| 861 | + if name == registration_name: |
| 862 | + callbacks.append(("update", type_, name)) |
| 863 | + |
| 864 | + listener = MyServiceListener() |
| 865 | + |
| 866 | + desc = {'path': '/~paulsm/'} |
| 867 | + address_parsed = "10.0.1.2" |
| 868 | + address = socket.inet_aton(address_parsed) |
| 869 | + info = ServiceInfo(type_, registration_name, 80, 0, 0, desc, "ash-2.local.", addresses=[address]) |
| 870 | + zc.cache.async_add_records( |
| 871 | + [info.dns_pointer(), info.dns_service(), *info.dns_addresses(), info.dns_text()] |
| 872 | + ) |
| 873 | + |
| 874 | + browser = AsyncServiceBrowser(zc, type_, None, listener) |
| 875 | + |
| 876 | + generated = DNSOutgoing(const._FLAGS_QR_RESPONSE) |
| 877 | + generated.add_answer_at_time( |
| 878 | + DNSPointer( |
| 879 | + "_unrelated._tcp.local.", |
| 880 | + const._TYPE_PTR, |
| 881 | + const._CLASS_IN, |
| 882 | + const._DNS_OTHER_TTL, |
| 883 | + "zoom._unrelated._tcp.local.", |
| 884 | + ), |
| 885 | + 0, |
| 886 | + ) |
| 887 | + generated.add_answer_at_time( |
| 888 | + DNSAddress( |
| 889 | + "zoom._unrelated._tcp.local.", const._TYPE_A, const._CLASS_IN, const._DNS_HOST_TTL, b"1234" |
| 890 | + ), |
| 891 | + 0, |
| 892 | + ) |
| 893 | + generated.add_answer_at_time( |
| 894 | + DNSService( |
| 895 | + "zoom._unrelated._tcp.local.", |
| 896 | + const._TYPE_SRV, |
| 897 | + const._CLASS_IN, |
| 898 | + const._DNS_HOST_TTL, |
| 899 | + 0, |
| 900 | + 0, |
| 901 | + 81, |
| 902 | + 'unrelated.local.', |
| 903 | + ), |
| 904 | + 0, |
| 905 | + ) |
| 906 | + |
| 907 | + zc.handle_response(DNSIncoming(generated.packets()[0])) |
| 908 | + |
| 909 | + await browser.async_cancel() |
| 910 | + await asyncio.sleep(0) |
| 911 | + |
| 912 | + assert callbacks == [ |
| 913 | + ('add', type_, registration_name), |
| 914 | + ] |
| 915 | + await aiozc.async_close() |
0 commit comments