@@ -140,32 +140,66 @@ def quick_timing() -> Generator[None]:
140140 """Shorten the probe/announce/goodbye/first-query intervals for tests on loopback.
141141
142142 The production values (_CHECK_TIME=500ms, _REGISTER_TIME=225ms,
143- _UNREGISTER_TIME=125ms, _FIRST_QUERY_DELAY_RANDOM_INTERVAL=20-120ms)
144- exist for RFC 6762 interop on real networks (§8.1 thundering-herd
145- avoidance for probing, §5.2 for the initial-query delay). Tests on
146- 127.0.0.1 do not need them and pay 1-2s per register/unregister
147- cycle and 20-120ms per ServiceBrowser startup without this fixture.
148- Opt in by adding `quick_timing` to a test's argument list.
143+ _UNREGISTER_TIME=125ms, _PROBE_RANDOM_DELAY_INTERVAL=150-250ms,
144+ _FIRST_QUERY_DELAY_RANDOM_INTERVAL=20-120ms) exist for RFC 6762
145+ interop on real networks (§8.1 thundering-herd avoidance for
146+ probing, §5.2 for the initial-query delay). Tests on 127.0.0.1
147+ do not need them and pay 1-2s per register/unregister cycle,
148+ 150-250ms per probe, and 20-120ms per ServiceBrowser startup
149+ without this fixture. Opt in either by adding `quick_timing`
150+ to a test's argument list or via
151+ `@pytest.mark.usefixtures("quick_timing")` on the test or
152+ its class.
149153 """
150154 with (
151155 patch .object (_core , "_CHECK_TIME" , 10 ),
152156 patch .object (_core , "_REGISTER_TIME" , 10 ),
153157 patch .object (_core , "_UNREGISTER_TIME" , 10 ),
158+ patch .object (_core , "_PROBE_RANDOM_DELAY_INTERVAL" , (1 , 5 )),
154159 patch .object (service_browser , "_FIRST_QUERY_DELAY_RANDOM_INTERVAL" , (1 , 5 )),
155160 ):
156161 yield
157162
158163
164+ @pytest .fixture
165+ def quick_aggregation_timing () -> Generator [None ]:
166+ """Scale multicast aggregation / network-protection delays 10x for tests.
167+
168+ The aggregation tests in `tests/test_handlers.py` verify timing-
169+ dependent behaviour of `MulticastOutgoingQueue`: aggregation window,
170+ network protection (~1s), and protected aggregation. The behaviour
171+ under test is a ratio of these constants — the exact wall-clock
172+ values are not the contract — so scaling them down and the test
173+ sleeps in lock-step preserves what is tested while dropping each
174+ test from ~3s to ~0.3s.
175+
176+ The patches must be in place before `AsyncZeroconf(...)` is
177+ constructed because `MulticastOutgoingQueue` reads the constants at
178+ init time and stashes them on the instance. The per-queue
179+ `_multicast_delay_random_min` / `_max` jitter (1-5ms here) can
180+ still be set on the queue instance after construction by the test
181+ itself — those slots are `cdef public` in the .pxd.
182+ """
183+ with (
184+ patch .object (_core , "_AGGREGATION_DELAY" , 50 ),
185+ patch .object (_core , "_PROTECTED_AGGREGATION_DELAY" , 20 ),
186+ patch .object (_core , "_ONE_SECOND" , 100 ),
187+ ):
188+ yield
189+
190+
159191@pytest .fixture
160192def quick_request_timing () -> Generator [None ]:
161193 """Shorten the initial-query delay used by AsyncServiceInfo.async_request.
162194
163195 The 200ms `_LISTENER_TIME` and 20-120ms random jitter (RFC 6762
164196 §5.2) help spread queries from multiple clients on real networks.
165197 On loopback they're pure overhead — get_service_info-style tests
166- wait ~250ms before the first query even fires. Opt in by adding
167- `quick_request_timing` to a test's argument list, then drop the
168- test's own timeouts (which had to accommodate that delay).
198+ wait ~250ms before the first query even fires. Opt in either by
199+ adding `quick_request_timing` to a test's argument list or via
200+ `@pytest.mark.usefixtures("quick_request_timing")` on the test
201+ or its class, then drop the test's own timeouts (which had to
202+ accommodate that delay).
169203 """
170204 with (
171205 patch .object (service_info , "_LISTENER_TIME" , 10 ),
0 commit comments