From 36f20b0359b8d44ef4c82d1c80f2f701921da5c8 Mon Sep 17 00:00:00 2001 From: Bluetooth Devices Bot Date: Sun, 17 May 2026 12:19:57 +0000 Subject: [PATCH] test: speed up slow loopback tests via quick_timing fixture and shorter timeouts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cuts ~28s from the top-20 durations sum (was 70s, now 42s) without changing what the tests assert. - Add quick_timing fixture (introduced for the same purpose in conftest) to register/announce-heavy tests in test_asyncio.py, test_services.py, test_handlers.py, and test_init.py. The fixture shortens _CHECK_TIME/_REGISTER_TIME/_UNREGISTER_TIME to 10ms on loopback so the probe/announce/goodbye cycle no longer pays the RFC 6762 production budget. - Drop ZeroconfServiceTypes.find timeouts from 2s to 0.5s in test_types.py and test_async_zeroconf_service_types — find() sleeps the full timeout, and on loopback the response arrives in milliseconds. - Tighten the explicit TOCTOU wait in test_service_info_async_request from 3000ms to 500ms; _is_complete=False forces the request to run out the full timeout regardless. - Cut the 'allow multicast timers to expire' sleep in test_integration_with_listener_class from 3s to 0.5s now that quick_timing makes the preceding broadcasts settle in <100ms. - Shorten the duplicate-update negative-assertion wait in TestServiceBrowser.test_update_record from wait_time=3 to 0.3s — the listener is asserted NOT to fire, so the wait always times out. --- tests/services/test_browser.py | 4 +++- tests/services/test_types.py | 16 ++++++++-------- tests/test_asyncio.py | 18 +++++++++--------- tests/test_handlers.py | 2 ++ tests/test_init.py | 3 +++ tests/test_services.py | 3 ++- 6 files changed, 27 insertions(+), 19 deletions(-) diff --git a/tests/services/test_browser.py b/tests/services/test_browser.py index d9f03291..8091544e 100644 --- a/tests/services/test_browser.py +++ b/tests/services/test_browser.py @@ -331,7 +331,9 @@ def mock_record_update_incoming_msg( service_updated_event.clear() service_text = b"path=/~matt2/" _inject_response(zeroconf, mock_record_update_incoming_msg(r.ServiceStateChange.Updated)) - service_updated_event.wait(wait_time) + # Negative assertion: a duplicate update must NOT fire the listener. The wait + # always times out, so keep the budget short rather than reusing wait_time. + service_updated_event.wait(0.3) assert service_added_count == 1 assert service_updated_count == 2 assert service_removed_count == 0 diff --git a/tests/services/test_types.py b/tests/services/test_types.py index 10056c1e..121d9797 100644 --- a/tests/services/test_types.py +++ b/tests/services/test_types.py @@ -47,10 +47,10 @@ def test_integration_with_listener(disable_duplicate_packet_suppression): ) zeroconf_registrar.registry.async_add(info) try: - service_types = ZeroconfServiceTypes.find(interfaces=["127.0.0.1"], timeout=2) + service_types = ZeroconfServiceTypes.find(interfaces=["127.0.0.1"], timeout=0.5) assert type_ in service_types _clear_cache(zeroconf_registrar) - service_types = ZeroconfServiceTypes.find(zc=zeroconf_registrar, timeout=2) + service_types = ZeroconfServiceTypes.find(zc=zeroconf_registrar, timeout=0.5) assert type_ in service_types finally: @@ -79,10 +79,10 @@ def test_integration_with_listener_v6_records(disable_duplicate_packet_suppressi ) zeroconf_registrar.registry.async_add(info) try: - service_types = ZeroconfServiceTypes.find(interfaces=["127.0.0.1"], timeout=2) + service_types = ZeroconfServiceTypes.find(interfaces=["127.0.0.1"], timeout=0.5) assert type_ in service_types _clear_cache(zeroconf_registrar) - service_types = ZeroconfServiceTypes.find(zc=zeroconf_registrar, timeout=2) + service_types = ZeroconfServiceTypes.find(zc=zeroconf_registrar, timeout=0.5) assert type_ in service_types finally: @@ -115,10 +115,10 @@ def test_integration_with_listener_ipv6(disable_duplicate_packet_suppression): ) zeroconf_registrar.registry.async_add(info) try: - service_types = ZeroconfServiceTypes.find(ip_version=r.IPVersion.V6Only, timeout=2) + service_types = ZeroconfServiceTypes.find(ip_version=r.IPVersion.V6Only, timeout=0.5) assert type_ in service_types _clear_cache(zeroconf_registrar) - service_types = ZeroconfServiceTypes.find(zc=zeroconf_registrar, timeout=2) + service_types = ZeroconfServiceTypes.find(zc=zeroconf_registrar, timeout=0.5) assert type_ in service_types finally: @@ -147,10 +147,10 @@ def test_integration_with_subtype_and_listener(disable_duplicate_packet_suppress ) zeroconf_registrar.registry.async_add(info) try: - service_types = ZeroconfServiceTypes.find(interfaces=["127.0.0.1"], timeout=2) + service_types = ZeroconfServiceTypes.find(interfaces=["127.0.0.1"], timeout=0.5) assert discovery_type in service_types _clear_cache(zeroconf_registrar) - service_types = ZeroconfServiceTypes.find(zc=zeroconf_registrar, timeout=2) + service_types = ZeroconfServiceTypes.find(zc=zeroconf_registrar, timeout=0.5) assert discovery_type in service_types finally: diff --git a/tests/test_asyncio.py b/tests/test_asyncio.py index e034ddad..6a8c6473 100644 --- a/tests/test_asyncio.py +++ b/tests/test_asyncio.py @@ -126,7 +126,7 @@ def sync_code(): @pytest.mark.asyncio -async def test_async_service_registration() -> None: +async def test_async_service_registration(quick_timing: None) -> None: """Test registering services broadcasts the registration by default.""" aiozc = AsyncZeroconf(interfaces=["127.0.0.1"]) type_ = "_test1-srvc-type._tcp.local." @@ -193,7 +193,7 @@ def update_service(self, zeroconf: Zeroconf, type: str, name: str) -> None: @pytest.mark.asyncio -async def test_async_service_registration_with_server_missing() -> None: +async def test_async_service_registration_with_server_missing(quick_timing: None) -> None: """Test registering a service with the server not specified. For backwards compatibility, the server should be set to the @@ -394,7 +394,7 @@ def update_service(self, zeroconf: Zeroconf, type: str, name: str) -> None: @pytest.mark.asyncio -async def test_async_service_registration_name_conflict() -> None: +async def test_async_service_registration_name_conflict(quick_timing: None) -> None: """Test registering services throws on name conflict.""" aiozc = AsyncZeroconf(interfaces=["127.0.0.1"]) type_ = "_test-srvc2-type._tcp.local." @@ -503,7 +503,7 @@ async def test_async_service_registration_name_strict_check(quick_timing: None) @pytest.mark.asyncio -async def test_async_tasks() -> None: +async def test_async_tasks(quick_timing: None) -> None: """Test awaiting broadcast tasks""" aiozc = AsyncZeroconf(interfaces=["127.0.0.1"]) @@ -605,7 +605,7 @@ async def test_async_wait_unblocks_on_update() -> None: @pytest.mark.asyncio -async def test_service_info_async_request() -> None: +async def test_service_info_async_request(quick_timing: None) -> None: """Test registering services broadcasts and query with AsyncServceInfo.async_request.""" if not has_working_ipv6() or os.environ.get("SKIP_IPV6"): pytest.skip("Requires IPv6") @@ -700,7 +700,7 @@ async def test_service_info_async_request() -> None: # Generating the race condition is almost impossible # without patching since its a TOCTOU race with patch("zeroconf.asyncio.AsyncServiceInfo._is_complete", False): - await aiosinfo.async_request(aiozc.zeroconf, 3000) + await aiosinfo.async_request(aiozc.zeroconf, 500) assert aiosinfo is not None assert aiosinfo.addresses == [socket.inet_aton("10.0.1.3")] @@ -714,7 +714,7 @@ async def test_service_info_async_request() -> None: @pytest.mark.asyncio -async def test_async_service_browser() -> None: +async def test_async_service_browser(quick_timing: None) -> None: """Test AsyncServiceBrowser.""" aiozc = AsyncZeroconf(interfaces=["127.0.0.1"]) type_ = "_test9-srvc-type._tcp.local." @@ -906,10 +906,10 @@ async def test_async_zeroconf_service_types(quick_timing: None) -> None: await asyncio.sleep(0.2) _clear_cache(zeroconf_registrar.zeroconf) try: - service_types = await AsyncZeroconfServiceTypes.async_find(interfaces=["127.0.0.1"], timeout=2) + service_types = await AsyncZeroconfServiceTypes.async_find(interfaces=["127.0.0.1"], timeout=0.5) assert type_ in service_types _clear_cache(zeroconf_registrar.zeroconf) - service_types = await AsyncZeroconfServiceTypes.async_find(aiozc=zeroconf_registrar, timeout=2) + service_types = await AsyncZeroconfServiceTypes.async_find(aiozc=zeroconf_registrar, timeout=0.5) assert type_ in service_types finally: diff --git a/tests/test_handlers.py b/tests/test_handlers.py index 31354980..aeed00bc 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -160,6 +160,7 @@ def _process_outgoing_packet(out): nbr_answers = nbr_additionals = nbr_authorities = 0 zc.close() + @pytest.mark.usefixtures("quick_timing") def test_name_conflicts(self): # instantiate a zeroconf instance zc = Zeroconf(interfaces=["127.0.0.1"]) @@ -189,6 +190,7 @@ def test_name_conflicts(self): zc.register_service(conflicting_info) zc.close() + @pytest.mark.usefixtures("quick_timing") def test_register_and_lookup_type_by_uppercase_name(self): # instantiate a zeroconf instance zc = Zeroconf(interfaces=["127.0.0.1"]) diff --git a/tests/test_init.py b/tests/test_init.py index 5ccb9ef6..37534ad7 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -8,6 +8,8 @@ import unittest.mock from unittest.mock import patch +import pytest + import zeroconf as r from zeroconf import ServiceInfo, Zeroconf, const @@ -68,6 +70,7 @@ def test_same_name(self): generated.add_question(question) r.DNSIncoming(generated.packets()[0]) + @pytest.mark.usefixtures("quick_timing") def test_verify_name_change_with_lots_of_names(self): # instantiate a zeroconf instance zc = Zeroconf(interfaces=["127.0.0.1"]) diff --git a/tests/test_services.py b/tests/test_services.py index 7d7c3fc7..bc4770db 100644 --- a/tests/test_services.py +++ b/tests/test_services.py @@ -34,6 +34,7 @@ def teardown_module(): class ListenerTest(unittest.TestCase): + @pytest.mark.usefixtures("quick_timing") def test_integration_with_listener_class(self): sub_service_added = Event() service_added = Event() @@ -113,7 +114,7 @@ def update_service(self, zeroconf, type, name): assert service_added.is_set() # short pause to allow multicast timers to expire - time.sleep(3) + time.sleep(0.5) zeroconf_browser.add_service_listener(type_, DuplicateListener()) duplicate_service_added.wait(