Skip to content

test: speed up slow loopback tests (closes #1697)#1699

Merged
bdraco merged 1 commit into
python-zeroconf:masterfrom
bluetoothbot:koan/speed-up-slow-tests
May 17, 2026
Merged

test: speed up slow loopback tests (closes #1697)#1699
bdraco merged 1 commit into
python-zeroconf:masterfrom
bluetoothbot:koan/speed-up-slow-tests

Conversation

@bluetoothbot

@bluetoothbot bluetoothbot commented May 17, 2026

Copy link
Copy Markdown
Contributor

What

Cut ~28s from the slowest-20 duration sum (70s → 42s) without changing
what any test asserts. All 336 tests still pass.

Why

Issue #1697 — the slow-test report listed twenty tests taking 2–10s
each, dominated by full mDNS probe/announce/goodbye cycles on
loopback. The project already ships a quick_timing fixture in
tests/conftest.py for this exact case (RFC 6762 timings don't need
to apply when talking to 127.0.0.1) — it just wasn't applied
everywhere.

How

  • Apply quick_timing to register/announce-heavy tests in
    tests/test_asyncio.py, tests/test_services.py,
    tests/test_handlers.py, and tests/test_init.py. For
    unittest.TestCase-based tests, wired in via
    @pytest.mark.usefixtures("quick_timing").
  • Drop ZeroconfServiceTypes.find timeouts from 2s to 0.5s in
    tests/services/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 of network latency.
  • 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 left untouched include test_async_updates_from_response
(needs the RFC 6762 §10.2 one-second invalidation window),
test_get_info_suppressed_by_question_history,
test_we_try_four_times_with_random_delay, and the
test_response_aggregation_timings* pair — those measure timing
behaviour and the real delays are the assertion.

Testing

  • poetry run pytest --durations=20 --timeout=60 — 336 passed,
    2 skipped, 83.5s (was 87.4s)
  • Verified each modified test individually still passes and shows a
    drop on --durations.
  • ruff check + ruff format --check clean.

Per-test before/after on the slowest entries from #1697:

Test Before After
test_service_info_async_request 9.83s 3.98s
test_integration_with_listener_class 6.83s 1.84s
test_async_zeroconf_service_types 4.49s 1.47s
test_integration_with_listener 4.28s 1.28s
test_integration_with_listener_v6_records 4.28s 1.28s
test_integration_with_subtype_and_listener 4.28s 1.27s
test_update_record (browser) 3.22s 0.54s
test_register_and_lookup_type_by_uppercase_name 2.88s 1.25s
test_async_service_registration 2.87s 0.37s
test_verify_name_change_with_lots_of_names 2.71s 0.70s
test_async_service_browser 2.40s 0.30s
test_async_tasks 2.39s 0.26s
test_async_service_registration_with_server_missing 2.37s 0.26s
test_async_service_registration_name_conflict 2.34s 0.70s
test_name_conflicts 2.20s 0.52s

Closes #1697


Quality Report

Changes: 6 files changed, 27 insertions(+), 19 deletions(-)

Code scan: clean

Tests: passed (4 PASSED)

Branch hygiene: clean

Generated by Kōan post-mission quality pipeline

…er timeouts

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.
@codspeed-hq

codspeed-hq Bot commented May 17, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 6 untouched benchmarks


Comparing bluetoothbot:koan/speed-up-slow-tests (36f20b0) with master (dfa4e00)

Open in CodSpeed

@codecov

codecov Bot commented May 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.76%. Comparing base (dfa4e00) to head (36f20b0).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1699   +/-   ##
=======================================
  Coverage   99.76%   99.76%           
=======================================
  Files          33       33           
  Lines        3410     3410           
  Branches      464      464           
=======================================
  Hits         3402     3402           
  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.

@bdraco

bdraco commented May 17, 2026

Copy link
Copy Markdown
Member

@bluetoothbot rr

@bluetoothbot

Copy link
Copy Markdown
Contributor Author

PR Review — test: speed up slow loopback tests (closes #1697)

Clean, well-scoped speed-up that exploits an existing fixture (quick_timing) rather than introducing new test infrastructure. Each change is justified in the PR body with before/after numbers, the untouched timing-sensitive tests (test_async_updates_from_response, test_response_aggregation_timings*, test_we_try_four_times_with_random_delay) are correctly excluded, and the one place where the new constant is non-obvious (negative-assertion wait in test_update_record) has an explanatory comment per project convention. No production code changes, coverage unchanged, CodSpeed unchanged. Only flag is potential CI flakiness on PyPy / 3.14t for the ZeroconfServiceTypes.find() 0.5s timeouts — that's a watch item, not a blocker. Merge-ready.


🟢 Suggestions

1. Watch for flakiness on slow CI matrix entries (`tests/services/test_types.py`, L50-53)

These tests register via zeroconf_registrar.registry.async_add(info) (direct registry add, no broadcast), so the announce path isn't the variable here — the only thing that has to happen inside the 0.5s window is the find() query and its response. On loopback that's milliseconds, so 0.5s is a 100x+ margin and almost certainly safe. The CI matrix runs PyPy 3.10 and the free-threaded 3.14t build though, which can be 5–10x slower under load. If you see flakes on those matrix entries after this lands, the cheap fix is to bump just these four find() calls to 1s rather than reverting — that's still 2x faster than the original and gives PyPy headroom. Not a blocker; flagging because none of the other tightened tests use find().

service_types = ZeroconfServiceTypes.find(interfaces=["127.0.0.1"], timeout=0.5)
2. Consider a one-liner comment for the magic number (`tests/test_services.py`, L117)

The existing comment on line 116 (# short pause to allow multicast timers to expire) is now misleading — 0.5s is no longer a "short pause" in absolute terms relative to what's being waited on, it's deliberately calibrated against the quick_timing budget (preceding broadcasts settle in <100ms, so 5x margin). The PR description captures this rationale, but a future reader looking only at this file won't see it. Per CLAUDE.md ("Don't remove existing comments unless the code they describe is gone") the existing comment should stay, but consider tightening it to something like # allow multicast timers to expire — quick_timing settles broadcasts in <100ms. Same story as your test_browser.py comment, which does the right thing.

            # short pause to allow multicast timers to expire
            time.sleep(0.5)

Checklist

  • No hardcoded secrets
  • Input validation at boundaries
  • Error handling — no bare except, no swallowed errors
  • Resource cleanup (sockets, zeroconf instances)
  • Test isolation — no shared state introduced
  • Tests validate observable behavior, not implementation text
  • Timeouts have adequate headroom for slow CI runners (PyPy, 3.14t) — suggestion #1
  • Non-obvious magic numbers carry a why-comment — suggestion #2
  • Conventional Commits subject (test: prefix)
  • No Co-Authored-By trailers from automated agents
  • No changes to Cythonized hot paths / .pxd files

Summary

Clean, well-scoped speed-up that exploits an existing fixture (quick_timing) rather than introducing new test infrastructure. Each change is justified in the PR body with before/after numbers, the untouched timing-sensitive tests (test_async_updates_from_response, test_response_aggregation_timings*, test_we_try_four_times_with_random_delay) are correctly excluded, and the one place where the new constant is non-obvious (negative-assertion wait in test_update_record) has an explanatory comment per project convention. No production code changes, coverage unchanged, CodSpeed unchanged. Only flag is potential CI flakiness on PyPy / 3.14t for the ZeroconfServiceTypes.find() 0.5s timeouts — that's a watch item, not a blocker. Merge-ready.


Automated review by Kōan36f20b0

@bluetoothbot

Copy link
Copy Markdown
Contributor Author

Rebase with requested adjustments

Branch koan/speed-up-slow-tests was rebased onto master and review feedback was applied.

Stats

6 files changed, 27 insertions(+), 19 deletions(-)
Actions performed
  • Already-solved check: negative (confidence=high, reasoning=Commit d5e1f01 added quick_timing fixture and applied it to some tests, but this PR extends applicat)
  • Rebased koan/speed-up-slow-tests onto upstream/master
  • Pre-push CI check: previous run passed
  • Force-pushed koan/speed-up-slow-tests to origin
  • CI check enqueued in ## CI (async)

CI status

CI will be checked asynchronously.


Automated by Kōan

@bdraco

bdraco commented May 17, 2026

Copy link
Copy Markdown
Member

@bluetoothbot rebase

@bdraco

bdraco commented May 17, 2026

Copy link
Copy Markdown
Member

@bluetoothbot rr

@bluetoothbot

Copy link
Copy Markdown
Contributor Author

Rebase with requested adjustments

Branch koan/speed-up-slow-tests was rebased onto master and review feedback was applied.

Stats

6 files changed, 27 insertions(+), 19 deletions(-)
Actions performed
  • Already-solved check: negative (confidence=high, reasoning=Master commit d5e1f01 added quick_timing fixture and applied it to some tests, but this PR extends a)
  • Rebased koan/speed-up-slow-tests onto upstream/master
  • Pre-push CI check: previous run passed
  • Force-pushed koan/speed-up-slow-tests to origin
  • CI check enqueued in ## CI (async)

CI status

CI will be checked asynchronously.


Automated by Kōan

@bdraco bdraco marked this pull request as ready for review May 17, 2026 15:02
@bdraco bdraco merged commit dd341a3 into python-zeroconf:master May 17, 2026
37 checks passed
@bluetoothbot bluetoothbot deleted the koan/speed-up-slow-tests branch May 17, 2026 15:40
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.

slow tests need to be sped up

2 participants