a32b6d6248
- **udp-loss-shim** body: tokio UDP loopback that drops a configurable fraction of datagrams. Single-tenant (one client at a time) — moq-lite is connection-multiplexed by source port, so 1:1 forwarding is enough. - **I9** (`packet_loss_1pct_does_not_kill_audio`): routes the Kotlin speaker through the shim with `--loss-rate 0.01`; asserts the decoded PCM has ≥ 80% expected sample count and the 440 Hz tone survives. moq-lite groups are reliable streams so retransmits absorb the loss. - **I5** (`speaker_hot_swap_does_not_crash`): drives the reconnecting-speaker with `tokenRefreshAfterMs = 2_500` to force a hot-swap mid-broadcast. The reference hang-listen is single-shot subscribe (it doesn't re-subscribe on broadcast re-announce), so it captures only the pre-swap chunk; the test asserts the speaker survives without corrupting active uni streams (≥ 1 s of audio + FFT peak at 440 Hz on the captured chunk). The "no audible gap" property the spec calls for is an Amethyst-listener concern (handles re-announce transparently); a Phase 3 follow-up would exercise that path end-to-end through `connectNestsListener`. I7 (publisher reconnect on the Rust side, ref→A) is the mirror image and would need hang-publish to take a "--reconnect-after-ms" flag, plus the Amethyst listener path through the harness — also Phase 3 follow-up. https://claude.ai/code/session_01ERJPUYfdLPwZ99pr5EcEcV
29 lines
784 B
TOML
29 lines
784 B
TOML
[package]
|
|
name = "udp-loss-shim"
|
|
version.workspace = true
|
|
edition.workspace = true
|
|
publish.workspace = true
|
|
license.workspace = true
|
|
|
|
# UDP loopback that drops a configurable fraction of datagrams.
|
|
# Used by the I9 packet-loss interop scenario:
|
|
#
|
|
# client (--server-bind 0.0.0.0:0) → udp-loss-shim --listen X
|
|
# → moq-relay --upstream Y
|
|
#
|
|
# The shim is a single-tenant relay (one client at a time) — moq-lite
|
|
# is on QUIC which is connection-multiplexed by the client's source
|
|
# port, so we forward 1:1.
|
|
|
|
[[bin]]
|
|
name = "udp-loss-shim"
|
|
path = "src/main.rs"
|
|
|
|
[dependencies]
|
|
anyhow.workspace = true
|
|
clap.workspace = true
|
|
tokio.workspace = true
|
|
rand = "0.8"
|
|
tracing = "0.1"
|
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|