Skip to content

test: add quick_timing fixture and apply to register-heavy tests#1678

Merged
bdraco merged 6 commits into
masterfrom
speedup-register-tests-quick-timing
May 15, 2026
Merged

test: add quick_timing fixture and apply to register-heavy tests#1678
bdraco merged 6 commits into
masterfrom
speedup-register-tests-quick-timing

Conversation

@bdraco
Copy link
Copy Markdown
Member

@bdraco bdraco commented May 15, 2026

Summary

Recent bump of _CHECK_TIME from 175ms to 500ms — made for
RFC 6762 interop on slow WiFi / IoT networks — inflated every
test that does real probe/announce/unregister by ~1–2s. The
production constants are not needed for loopback tests; services
register and answer themselves over 127.0.0.1 with no contention.

Adds an opt-in quick_timing fixture and applies it (plus a few
shorter explicit timeouts) to seven of the worst-offending
register-heavy tests. Combined wall time drops from ~46s to ~7.5s.

Details

tests/conftest.py

New quick_timing fixture patches three module-level constants
on zeroconf._core:

Constant Production value Test value
_CHECK_TIME 500 ms 10 ms
_REGISTER_TIME 225 ms 10 ms
_UNREGISTER_TIME 125 ms 10 ms

Patching at the _core module is required because these are
imported via from .const import … at module load time, so a
patch.object(const, …) does not rebind the name _core already
holds. Same reason disable_duplicate_packet_suppression patches
const directly — both styles already coexist in this file.

The fixture yields nothing; the type hint is
Generator[None, None, None].

Tests updated

Each of these now opts into quick_timing (and where relevant,
passes timeout=200 to a request that would otherwise wait the
default 3000ms):

Test Before After
tests/test_core.py::test_shutdown_while_register_in_process 10.69s 0.40s
tests/test_asyncio.py::test_service_info_async_request 9.82s ~0.7s
tests/test_asyncio.py::test_async_zeroconf_service_types 6.16s ~4.3s
tests/test_asyncio.py::test_async_unregister_all_services 5.99s 0.52s
tests/test_asyncio.py::test_async_service_registration_same_server_same_ports 3.23s ~0.2s
tests/test_asyncio.py::test_async_service_registration_same_server_different_ports 3.18s ~0.2s
tests/test_asyncio.py::test_async_service_registration_name_strict_check 2.99s ~0.2s

test_async_zeroconf_service_types only drops to ~4.3s because
its body still calls AsyncZeroconfServiceTypes.async_find(..., timeout=2) twice — that 2s is the property under test and is
not changed.

Why opt-in instead of autouse

tests/test_handlers.py::test_response_aggregation_timings and
test_response_aggregation_timings_multiple assert against the
production timing constants and must not be patched. Other slow
register-heavy tests can opt in incrementally in follow-up PRs.

Test plan

  • All seven modified tests pass:
    poetry run pytest tests/test_asyncio.py::test_async_service_registration_same_server_different_ports tests/test_asyncio.py::test_async_service_registration_same_server_same_ports tests/test_asyncio.py::test_async_service_registration_name_strict_check tests/test_asyncio.py::test_async_zeroconf_service_types tests/test_asyncio.py::test_service_info_async_request tests/test_asyncio.py::test_async_unregister_all_services tests/test_core.py::test_shutdown_while_register_in_process
    runs in 7.48s combined (was ~46s).
  • Pre-commit (ruff, mypy, etc.) clean.
  • test_response_aggregation_timings* are untouched — they
    still see the production constants.

bdraco and others added 2 commits May 15, 2026 13:10
Recent bump of _CHECK_TIME from 175ms to 500ms (made for RFC 6762
interop on slow WiFi / IoT networks) inflated every test that does
real probe/announce/unregister by ~1-2s. The production constants
are not needed for loopback tests — services register and answer
themselves over 127.0.0.1 with no contention.

Add an opt-in `quick_timing` fixture in tests/conftest.py that
patches `_core._CHECK_TIME`, `_core._REGISTER_TIME`, and
`_core._UNREGISTER_TIME` to 10ms each. Patching at the `_core`
module is required because these constants are pulled in via
`from .const import ...` at module load time, so patching
`const.*` does not affect the binding `_core` already holds.

Apply to seven of the worst-offending register-heavy tests, also
passing explicit `timeout=200` to the post-unregister
`async_get_service_info` calls and the patched-_is_complete
`async_request` call so they don't pay the default 3000ms.

Combined wall time across the seven tests drops from ~46s to
~7.5s:

  test_shutdown_while_register_in_process   10.69s -> 0.40s
  test_service_info_async_request            9.82s -> 0.7s
  test_async_zeroconf_service_types          6.16s -> 4.3s
  test_async_unregister_all_services         5.99s -> 0.52s
  test_async_service_registration_same_server_same_ports       3.23s -> ~0.2s
  test_async_service_registration_same_server_different_ports  3.18s -> ~0.2s
  test_async_service_registration_name_strict_check            2.99s -> ~0.2s

The fixture is opt-in (not autouse) because a handful of tests
under tests/test_handlers.py (`test_response_aggregation_timings`,
`test_response_aggregation_timings_multiple`) assert against the
production timing constants and must not be patched.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.76%. Comparing base (1b31ed5) to head (5ab917f).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1678   +/-   ##
=======================================
  Coverage   99.76%   99.76%           
=======================================
  Files          33       33           
  Lines        3401     3401           
  Branches      461      461           
=======================================
  Hits         3393     3393           
  Misses          5        5           
  Partials        3        3           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 15, 2026

Merging this PR will not alter performance

✅ 6 untouched benchmarks


Comparing speedup-register-tests-quick-timing (5ab917f) with master (1b31ed5)

Open in CodSpeed

bdraco and others added 4 commits May 15, 2026 13:53
The previous commit shortened two timeouts in this test
(`async_request(zc, 3000)` and the post-unregister
`async_get_service_info(..., timeout=200)`). The first one is the
patched `_is_complete = False` block — the in-tree comment notes
this exercises a TOCTOU race that 'is almost impossible to
generate without patching.' Shortening the timeout shrinks the
TOCTOU window enough that on slower CI runners (ubuntu 3.10
skip_cython, windows 3.9 skip_cython) the listener resolves to a
stale pre-update broadcast (10.0.1.2) before the post-update
record (10.0.1.3) arrives, and the assertion fails
deterministically.

Revert both timeouts to their defaults. The `quick_timing`
fixture stays — it still cuts the register/update phases from
~6s to ~0.5s, so the test goes from ~9.82s to ~6.6s (3.3s
saved) without reintroducing the race.
The previous revert kept the production timeouts but left
quick_timing in place. CI still failed on 3.14t macOS skip_cython
with the same assertion (`addresses == [10.0.1.2]` instead of
`[10.0.1.3]`) — so the accelerated probe/announce intervals
themselves race with the post-update broadcast on slower / pure-
Python runners. With `_REGISTER_TIME=10ms`, the new info's
announces can collide with still-in-flight broadcasts of the
original (10.0.1.2) record and the listener ends up with the
stale address.

The test is exercising a TOCTOU race that needs the production
timing to behave deterministically. Drop the fixture from this
test entirely; it goes back to ~9.82s. The other six tests in
the PR still benefit from quick_timing.
@bdraco bdraco merged commit d5e1f01 into master May 15, 2026
42 checks passed
@bdraco bdraco deleted the speedup-register-tests-quick-timing branch May 15, 2026 22:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant