@@ -1601,14 +1601,23 @@ async def test_duplicate_goodbye_answers_in_packet():
16011601
16021602
16031603@pytest .mark .asyncio
1604- async def test_response_aggregation_timings (run_isolated ):
1605- """Verify multicast responses are aggregated."""
1604+ @pytest .mark .usefixtures ("quick_aggregation_timing" )
1605+ async def test_response_aggregation_timings (run_isolated : None ) -> None :
1606+ """Verify multicast responses are aggregated.
1607+
1608+ Aggregation / network-protection constants are scaled 10x by
1609+ ``quick_aggregation_timing``; the asserted ratios are unchanged
1610+ but each phase finishes in ~1/10 the wall time.
1611+ """
16061612 type_ = "_mservice._tcp.local."
16071613 type_2 = "_mservice2._tcp.local."
16081614 type_3 = "_mservice3._tcp.local."
16091615
16101616 aiozc = AsyncZeroconf (interfaces = ["127.0.0.1" ])
16111617 await aiozc .zeroconf .async_wait_for_start ()
1618+ for queue in (aiozc .zeroconf .out_queue , aiozc .zeroconf .out_delay_queue ):
1619+ queue ._multicast_delay_random_min = 1
1620+ queue ._multicast_delay_random_max = 5
16121621
16131622 name = "xxxyyy"
16141623 registration_name = f"{ name } .{ type_ } "
@@ -1673,9 +1682,10 @@ async def test_response_aggregation_timings(run_isolated):
16731682 protocol .datagram_received (query .packets ()[0 ], ("127.0.0.1" , const ._MDNS_PORT ))
16741683 protocol .datagram_received (query2 .packets ()[0 ], ("127.0.0.1" , const ._MDNS_PORT ))
16751684 protocol .datagram_received (query .packets ()[0 ], ("127.0.0.1" , const ._MDNS_PORT ))
1676- await asyncio .sleep (0.7 )
1685+ await asyncio .sleep (0.07 )
16771686
1678- # Should aggregate into a single answer with up to a 500ms + 120ms delay
1687+ # Should aggregate into a single answer with up to a 50ms + 5ms delay
1688+ # (scaled from 500ms + 120ms by `quick_aggregation_timing`).
16791689 calls = send_mock .mock_calls
16801690 assert len (calls ) == 1
16811691 outgoing = send_mock .call_args [0 ][0 ]
@@ -1686,10 +1696,10 @@ async def test_response_aggregation_timings(run_isolated):
16861696 send_mock .reset_mock ()
16871697
16881698 protocol .datagram_received (query3 .packets ()[0 ], ("127.0.0.1" , const ._MDNS_PORT ))
1689- await asyncio .sleep (0.3 )
1699+ await asyncio .sleep (0.03 )
16901700
1691- # Should send within 120ms since there are no other
1692- # answers to aggregate with
1701+ # Should send within 12ms (scaled max random delay) since there are
1702+ # no other answers to aggregate with.
16931703 calls = send_mock .mock_calls
16941704 assert len (calls ) == 1
16951705 outgoing = send_mock .call_args [0 ][0 ]
@@ -1698,21 +1708,21 @@ async def test_response_aggregation_timings(run_isolated):
16981708 assert info3 .dns_pointer () in incoming .answers ()
16991709 send_mock .reset_mock ()
17001710
1701- # Because the response was sent in the last second we need to make
1702- # sure the next answer is delayed at least a second
1711+ # Because the response was sent in the last 100ms (scaled 1s) we
1712+ # need to make sure the next answer is delayed at least that long.
17031713 aiozc .zeroconf .engine .protocols [0 ].datagram_received (
17041714 query4 .packets ()[0 ], ("127.0.0.1" , const ._MDNS_PORT )
17051715 )
1706- await asyncio .sleep (0.5 )
1716+ await asyncio .sleep (0.05 )
17071717
1708- # After 0.5 seconds it should not have been sent
1718+ # After 50ms it should not have been sent.
17091719 # Protect the network against excessive packet flooding
17101720 # https://datatracker.ietf.org/doc/html/rfc6762#section-14
17111721 calls = send_mock .mock_calls
17121722 assert len (calls ) == 0
17131723 send_mock .reset_mock ()
17141724
1715- await asyncio .sleep (1.2 )
1725+ await asyncio .sleep (0.12 )
17161726 calls = send_mock .mock_calls
17171727 assert len (calls ) == 1
17181728 outgoing = send_mock .call_args [0 ][0 ]
@@ -1723,14 +1733,30 @@ async def test_response_aggregation_timings(run_isolated):
17231733
17241734
17251735@pytest .mark .asyncio
1726- async def test_response_aggregation_timings_multiple (run_isolated , disable_duplicate_packet_suppression ):
1727- """Verify multicast responses that are aggregated do not take longer than 620ms to send.
1728-
1729- 620ms is the maximum random delay of 120ms and 500ms additional for aggregation."""
1736+ @pytest .mark .usefixtures ("quick_aggregation_timing" )
1737+ async def test_response_aggregation_timings_multiple (
1738+ run_isolated : None , disable_duplicate_packet_suppression : None
1739+ ) -> None :
1740+ """Verify multicast responses that are aggregated do not take longer than 62ms to send.
1741+
1742+ Aggregation / network-protection constants are scaled 10x by
1743+ ``quick_aggregation_timing`` (500ms→50ms, 200ms→20ms, 1000ms→100ms)
1744+ and the per-queue jitter is set to 1-5ms below. The asserted
1745+ ratios are the same as the production behaviour the test pins —
1746+ aggregation window, network protection, protected aggregation —
1747+ only the absolute durations are scaled.
1748+ """
17301749 type_2 = "_mservice2._tcp.local."
17311750
17321751 aiozc = AsyncZeroconf (interfaces = ["127.0.0.1" ])
17331752 await aiozc .zeroconf .async_wait_for_start ()
1753+ # Scale the queues' random jitter to match the 10x scaled
1754+ # additional / aggregation delays; without this, the 20-120ms
1755+ # jitter would dominate the scaled window and make timing assertions
1756+ # unreliable.
1757+ for queue in (aiozc .zeroconf .out_queue , aiozc .zeroconf .out_delay_queue ):
1758+ queue ._multicast_delay_random_min = 1
1759+ queue ._multicast_delay_random_max = 5
17341760
17351761 name = "xxxyyy"
17361762 registration_name2 = f"{ name } .{ type_2 } "
@@ -1760,7 +1786,7 @@ async def test_response_aggregation_timings_multiple(run_isolated, disable_dupli
17601786 protocol .datagram_received (query2 .packets ()[0 ], ("127.0.0.1" , const ._MDNS_PORT ))
17611787 protocol .last_time = 0 # manually reset to avoid duplicate packet suppression
17621788 protocol ._recent_packets .clear ()
1763- await asyncio .sleep (0.2 )
1789+ await asyncio .sleep (0.02 )
17641790 calls = send_mock .mock_calls
17651791 assert len (calls ) == 1
17661792 outgoing = send_mock .call_args [0 ][0 ]
@@ -1772,7 +1798,7 @@ async def test_response_aggregation_timings_multiple(run_isolated, disable_dupli
17721798 protocol .datagram_received (query2 .packets ()[0 ], ("127.0.0.1" , const ._MDNS_PORT ))
17731799 protocol .last_time = 0 # manually reset to avoid duplicate packet suppression
17741800 protocol ._recent_packets .clear ()
1775- await asyncio .sleep (1.2 )
1801+ await asyncio .sleep (0.12 )
17761802 calls = send_mock .mock_calls
17771803 assert len (calls ) == 1
17781804 outgoing = send_mock .call_args [0 ][0 ]
@@ -1787,19 +1813,19 @@ async def test_response_aggregation_timings_multiple(run_isolated, disable_dupli
17871813 protocol .datagram_received (query2 .packets ()[0 ], ("127.0.0.1" , const ._MDNS_PORT ))
17881814 protocol .last_time = 0 # manually reset to avoid duplicate packet suppression
17891815 protocol ._recent_packets .clear ()
1790- # The minimum protected send_after is 1000ms + 20ms random; sleep
1791- # well under that so coarse timers on slow runners cannot push the
1792- # send into this window and flake the assertion.
1793- await asyncio .sleep (0.5 )
1816+ # Scaled: minimum protected send_after is 100ms + 1-5ms random;
1817+ # sleep well under that so coarse timers on slow runners cannot
1818+ # push the send into this window and flake the assertion.
1819+ await asyncio .sleep (0.05 )
17941820 calls = send_mock .mock_calls
17951821 assert len (calls ) == 0
17961822
1797- # 1000ms ( 1s network protection delays )
1798- # - 500ms (already slept)
1799- # + 120ms ( maximum random delay)
1800- # + 200ms (maximum protected aggregation delay)
1801- # + 20ms (execution time )
1802- await asyncio .sleep (millis_to_seconds (1000 - 500 + 120 + 200 + 20 ))
1823+ # 100ms (scaled 1s network protection)
1824+ # - 50ms (already slept)
1825+ # + 5ms (scaled maximum random delay)
1826+ # + 20ms (scaled protected aggregation delay)
1827+ # + 5ms (execution slack )
1828+ await asyncio .sleep (millis_to_seconds (100 - 50 + 5 + 20 + 5 ))
18031829 calls = send_mock .mock_calls
18041830 assert len (calls ) == 1
18051831 outgoing = send_mock .call_args [0 ][0 ]
0 commit comments