@@ -597,18 +597,21 @@ def ClientServerExpectedFailureTest( server_extra_args=[], client_extra_args=[],
597597 'udp' , 1 , _CTR_DIRECT_NO_TURN ,
598598 ( _CAND_IPV6_NAT , _CAND_IPV6_NAT ) ),
599599
600- # Packet loss: verify that connection succeeds despite 20% outbound packet loss.
601- # Route type is intentionally not checked. With outbound-only loss, the relay
602- # path has a higher per-attempt round-trip success rate than the direct path:
603- # the relay response arrives via the TURN server's inbound socket, bypassing the
604- # lossy adapter, whereas the direct-path response must traverse the remote peer's
605- # lossy adapter. Under aggressive nomination (first pair to succeed wins), relay
606- # can beat the higher-priority direct path to nomination. This is current
607- # behaviour; route optimisation can be addressed later.
600+ # Packet loss: relay can win the initial nomination race over the direct path
601+ # (relay responses bypass the lossy adapter; see the route-upgrade commit for the
602+ # full analysis), but the ICE client now continues probing higher-priority pairs
603+ # after selection and upgrades when one succeeds. We therefore expect to end up
604+ # on 'udp' (direct srflx path).
605+ #
606+ # Spurious-failure analysis (at the time of this writing):
607+ # Each STUN round trip succeeds with P = 0.8 * 0.8 = 0.64 under 20% outbound
608+ # loss per side. The request schedule is 5 total sends (1 initial + 4 retx).
609+ # P(all 5 fail) = 0.36^5 ~= 0.6%. Triggered checks from the remote side add
610+ # extra attempts, so the real spurious-failure rate is somewhat below 0.6%.
608611 ( 'full-cone NAT, 20% packet loss' ,
609612 [ '--mock-loss' , '20' ] + _nat ( _SRV_INT , _SRV_GW , 'full-cone' ),
610613 [ '--mock-loss' , '20' ] + _nat ( _CLI_INT , _CLI_GW , 'full-cone' ),
611- None , 1 , None , None ),
614+ 'udp' , 1 , _CTR_DIRECT , None ),
612615
613616 # Signaling impairment: verify connection succeeds despite lossy or duplicate
614617 # signals. Uses real loopback (no mock network), so the route is always 'local'.
0 commit comments