Skip to content

test: speed up slow loopback tests (closes #1700)#1703

Merged
bdraco merged 1 commit into
masterfrom
koan/speed-up-slow-tests-1700
May 17, 2026
Merged

test: speed up slow loopback tests (closes #1700)#1703
bdraco merged 1 commit into
masterfrom
koan/speed-up-slow-tests-1700

Conversation

@bdraco
Copy link
Copy Markdown
Member

@bdraco bdraco commented May 17, 2026

Summary

Closes #1700. Drops full-suite runtime from ~85s to ~46s by
removing real-time waits from tests that don't need them on a
loopback socket. Behaviour under test is unchanged — only the
test setup is shortened.

Details

  • quick_timing fixture applied to 13 register/announce-heavy
    tests (test_register_service_with_custom_ttl,
    test_logging_packets, test_ptr_optimization, test_qu_response,
    test_async_context_manager, test_integration in
    test_asyncio.py, test_asking_default_is_asking_qm_questions_after_the_first_qu,
    test_ttl_refresh_cancelled_rescue_query, both
    test_legacy_record_update_listeners,
    test_close_zeroconf_without_browser_before_start_up_queries,
    test_close_zeroconf_without_browser_after_start_up_queries).
    The fixture shrinks _CHECK_TIME / _REGISTER_TIME /
    _UNREGISTER_TIME from RFC 6762 §8.1/§8.3 production values
    (500ms / 225ms / 125ms) to 10ms each via patch.object, so
    nothing outside the with block is affected.

  • _backdate_cache(zc, ms=1100) new helper in tests/__init__.py.
    Replaces real-time sleeps that exist only to satisfy RFC 6762 §10.2's
    "received more than one second ago" check (the cache flush logic in
    DNSCache.async_mark_unique_records_older_than_1s_to_expire):

    • test_async_updates_from_response: 3 × time.sleep(1.1) → 3 ×
      _backdate_cache(zc) (3.3s → 0.03s).
    • test_cache_flush_bit: await asyncio.sleep(1.1) → inline
      backdate of the TTL=1 records (1.1s → 0.0s).

    The helper iterates store.values() rather than the inner dict
    directly because DNSCache._async_add re-keys the dict with
    store[record] = record: when a new record with equal hash
    arrives, the key stays the original object but the value
    becomes the new record. Mutating the keys would update stale
    objects no one reads.

  • Reaper-driven tests (test_reaper, test_reaper_aborts_when_done,
    test_service_browser_expire_callbacks) re-add records via
    cache._async_set_created_ttl(record, past, 1) so the cache's
    expire heap picks up the updated when = created + ttl*1000.
    Mutating record.created alone leaves the heap entry pointing
    at the original future expiration time and the reaper never wakes
    the record up. Combined with the _CACHE_CLEANUP_INTERVAL=0.01s
    patch these tests already had, the reaper now fires within ~10ms.

  • test_service_info_async_request caps the post-unregister
    async_get_service_info to timeout=200 (the service is gone,
    the default 3000ms is pure overhead) and tightens the
    _is_complete=False TOCTOU-race loop from 3000ms to 1500ms.
    1500ms covers the initial _LISTENER_TIME + random_delay
    (≈220-320ms) and leaves plenty of margin for the loopback
    response to land before the loop times out.

  • test_info_asking_default_is_asking_qm_questions_after_the_first_qu
    drops its forced-loop timeout from 1200ms to 500ms — enough for
    the QU query at t=0 and the QM query at t≈_LISTENER_TIME +
    max random delay (320ms).

  • test_register_and_lookup_type_by_uppercase_name replaces
    time.sleep(1) with a 50×20ms poll on the cache (same pattern
    as test_sending_unicast in test_core.py).

  • test_integration_with_listener_class drains the registrar's
    out_queue / out_delay_queue instead of time.sleep(0.5) for
    "multicast timers to expire" — same pattern as test: drop pending multicast responses before TOCTOU assertion #1701.

The remaining ~2-3s tests in the slowest-20 are intrinsic-timing:
_DUPLICATE_QUESTION_INTERVAL-bound queries
(test_we_try_four_times_with_random_delay,
test_get_info_suppressed_by_question_history,
test_get_info_partial), the multicast aggregation windows
(test_response_aggregation_timings, …_multiple), and the 1s
asyncio.run_coroutine_threadsafe(...).result(1) guard in
test_shutdown_loop. _DUPLICATE_QUESTION_INTERVAL is a cdef
constant in _services/info.pxd and _history.pxd, so it can't
be patch.object-ed in Cython mode.

Test plan

  • Full suite passes: 335 passed, 3 skipped.
  • All changed tests stable across five back-to-back runs.
  • Pre-commit clean on the modified files (ruff, mypy, flake8,
    codespell, pyupgrade).
  • Before/after slowest-20 comparison shows the targeted tests
    now drop off the list (most go from ~1.9-3.3s down to
    0.03-0.4s).

Drops full-suite runtime from ~85s to ~46s by removing real-time
waits from tests that don't need them on a loopback socket.

- Apply the `quick_timing` fixture to 13 register/announce-heavy
  tests so probe/announce intervals shrink from 500ms/225ms/125ms
  to 10ms each. The tests pin behaviour, not the RFC 6762 timing
  constants, so the production values stay in place for everything
  outside the fixture's `with patch.object(...)` block.
- Introduce `_backdate_cache(zc, ms=1100)` in `tests/__init__.py`.
  `test_async_updates_from_response` and the inline backdate in
  `test_cache_flush_bit` use it to satisfy RFC 6762 §10.2's
  "received more than one second ago" check without `time.sleep`.
  The helper iterates `store.values()`, not the inner dict
  directly — when a record is re-added with an equal hash, the
  dict key stays the original object while the value gets the
  newer one, so mutating the keys would touch stale objects no
  one reads.
- `test_service_browser_expire_callbacks` and `test_reaper` re-add
  records via `_async_set_created_ttl(past, 1)` so the expire heap
  picks up the new `when`; mutating `record.created` alone leaves
  the heap entry pointing at the original future expiration time
  and the reaper never wakes the record up.
- `test_service_info_async_request` caps the post-unregister
  `async_get_service_info` to 200ms (the service is gone, the
  default 3000ms wait is pure overhead) and tightens the
  `_is_complete=False` TOCTOU-race loop to 1500ms — enough for
  the initial `_LISTENER_TIME + random_delay` (≈220-320ms) plus
  margin for the loopback response.
- `test_info_asking_default_is_asking_qm_questions_after_the_first_qu`
  drops its forced-loop timeout from 1200ms to 500ms (covers the
  QU at t=0 and the QM at t≈_LISTENER_TIME + max random delay).
- `test_register_and_lookup_type_by_uppercase_name` replaces
  `time.sleep(1)` with a 50×20ms poll on the cache.
- `test_integration_with_listener_class` drains pending multicast
  responses on the registrar instead of sleeping for them, same
  pattern as #1701.

All 335 tests still pass and all changed tests are stable across
five back-to-back runs.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.76%. Comparing base (653c385) to head (d84f83d).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1703   +/-   ##
=======================================
  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.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 17, 2026

Merging this PR will not alter performance

✅ 6 untouched benchmarks


Comparing koan/speed-up-slow-tests-1700 (d84f83d) with master (653c385)

Open in CodSpeed

@bdraco bdraco merged commit d03ea36 into master May 17, 2026
37 checks passed
@bdraco bdraco deleted the koan/speed-up-slow-tests-1700 branch May 17, 2026 19:48
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.

Many tests still remain slow

1 participant