test: shave loopback timing overhead from remaining slow tests#1760
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1760 +/- ##
=======================================
Coverage 99.77% 99.77%
=======================================
Files 33 33
Lines 3508 3509 +1
Branches 493 493
=======================================
+ Hits 3500 3501 +1
Misses 5 5
Partials 3 3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Reduces end-to-end runtime of several loopback-based tests by patching production-grade timing delays (probe/jitter/timeouts) down to test-friendly values while keeping RFC-timing tests unchanged.
Changes:
- Extracted the probe collision-avoidance jitter into
_PROBE_RANDOM_DELAY_INTERVALand patched it in thequick_timingfixture. - Added
quick_request_timingto selected tests and shortened an intentional timeout intest_service_info_async_request. - Reduced shutdown-loop waiting overhead by patching
_TASK_AWAIT_TIMEOUTin a loop shutdown test.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/utils/test_asyncio.py | Speeds up shutdown-loop test by shrinking sleeps/timeouts and patching _TASK_AWAIT_TIMEOUT. |
| tests/test_services.py | Applies quick_request_timing fixture to a listener integration test. |
| tests/test_asyncio.py | Applies quick_request_timing and reduces a forced timeout in an async service-info request test. |
| tests/services/test_info.py | Applies quick_request_timing to a partial info lookup test. |
| tests/conftest.py | Updates quick_timing fixture to also patch the new probe-delay interval. |
| src/zeroconf/_core.py | Introduces _PROBE_RANDOM_DELAY_INTERVAL and uses it for pre-probe random wait. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
PR Review — test: shave loopback timing overhead from remaining slow testsClean test-speedup PR. The 🟢 Suggestions1. Task-scheduling race in _run_coro thread (`tests/utils/test_asyncio.py`, L89-95)Good fix moving If you want to make this fully deterministic, have task_started = threading.Event()
async def _still_running():
task_started.set()
await asyncio.sleep(5)
# main thread
runcoro_thread_ready.wait()
task_started.wait()
with patch.object(aioutils, "_TASK_AWAIT_TIMEOUT", 0.05):
aioutils.shutdown_loop(loop)Not a blocker — the original code had the same class of race buffered by 2. Comment can be tightened to one line (`src/zeroconf/_core.py`, L107-110)Per CLAUDE.md ( # RFC 6762 §8.1: random 0-250ms pre-probe delay against thundering herd.
_PROBE_RANDOM_DELAY_INTERVAL = (150, 250) # msThe Checklist
SummaryClean test-speedup PR. The |
Rebase with requested adjustmentsBranch Changes applied
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
a958dcd to
a30eb88
Compare
Issue python-zeroconf#1707 listed 20 tests over 0.5s. The fixes here cut the ones that aren't pinning RFC behavior: - Extract the 150-250ms probe collision-avoidance delay in Zeroconf._async_check_service to _PROBE_RANDOM_DELAY_INTERVAL so the existing quick_timing fixture can shrink it on loopback. Drops 150-250ms per register_service call. - Add quick_request_timing to test_get_info_partial, test_integration_with_listener_class, and test_service_info_async_request — get_service_info calls no longer pay the 200ms _LISTENER_TIME + 20-120ms jitter. - Tighten the deliberate forced-timeout in test_service_info_async_request from 1500ms to 300ms now that quick_request_timing brings the first QM into the window in <20ms. - Patch _TASK_AWAIT_TIMEOUT in test_shutdown_loop so the asyncio.wait on the never-completing _still_running() task returns at 50ms instead of the default 1s. Tests that pin RFC 6762 timing (response aggregation, duplicate-question suppression, TC-deferral, 4-retry budget) are intentionally left alone — their runtime is the assertion.
Rebase with requested adjustmentsBranch StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
a30eb88 to
c8e442d
Compare
Summary
Issue #1707 listed 20 tests over 0.5s. Several of them weren't pinning RFC behavior — they were paying production-grade delays on a 127.0.0.1 fake network. This PR cuts the ones that can be safely shortened and leaves the RFC-pinned ones (response aggregation, duplicate-question suppression, TC-deferral, 4-retry budget) alone.
Fixes #1707
Changes
Zeroconf._async_check_serviceinto a new module-level_PROBE_RANDOM_DELAY_INTERVALconstant (src/zeroconf/_core.py). The existingquick_timingfixture now patches it to(1, 5), dropping ~200ms perregister_servicecall on loopback. Production behaviour unchanged.quick_request_timingto:tests/services/test_info.py::TestServiceInfo::test_get_info_partialtests/test_services.py::ListenerTest::test_integration_with_listener_classtests/test_asyncio.py::test_service_info_async_request…so
get_service_infocalls no longer pay the 200ms_LISTENER_TIME+ 20-120ms jitter on loopback.test_service_info_async_requestfrom 1500ms to 300ms — underquick_request_timingthe first QM lands in <20ms, so 300ms is comfortable._TASK_AWAIT_TIMEOUTto 50ms intest_shutdown_loopso the innerasyncio.waiton the deliberately never-completing_still_running()task returns quickly instead of blocking the default 1s.Slow test before / after (local measurements, top of
--durations=20for the issue's list)test_service_info_async_requesttest_get_info_partialtest_integration_with_listener_classtest_shutdown_looptest_get_info_singletest_async_zeroconf_service_typestest_async_service_registration_name_conflicttest_verify_name_change_with_lots_of_namestest_name_conflictstest_integration_with_listener(types)test_integration_with_subtype_and_listenertest_integration_with_listener_v6_recordstest_asking_qu_questionsTests intentionally left at their original runtime because they pin RFC 6762 timing:
test_response_aggregation_timings/_multiple(§14)test_we_try_four_times_with_random_delay(3000ms / 4 retries)test_get_info_suppressed_by_question_history(§7.1,_DUPLICATE_QUESTION_INTERVAL)test_tc_bit_defers_last_response_missing(§18.5,_TC_DELAY_RANDOM_INTERVAL)Test plan
poetry run pytest --timeout=60 -q— 385 passed, 2 skipped, no regressionspoetry run ruff check+poetry run ruff format --checkclean on touched filesGenerated by Kōan /fix
Quality Report
Changes: 6 files changed, 32 insertions(+), 17 deletions(-)
Code scan: clean
Tests: passed (4 PASSED)
Branch hygiene: clean
Generated by Kōan post-mission quality pipeline