Skip to content

Commit d7c1112

Browse files
kixelatedclaude
andcommitted
moq-native: e2e test for Lite05 TrackConsumer::info() over a session
Resolves a track's immutable properties via a TRACK stream (info(), no subscription), then subscribes and reads a frame. Guards the on-demand info path end-to-end ahead of the model refactor. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent f37b862 commit d7c1112

1 file changed

Lines changed: 98 additions & 0 deletions

File tree

rs/moq-native/tests/broadcast.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,104 @@ async fn broadcast_moq_lite_05_timestamps_webtransport() {
227227
lite05_timestamp_roundtrip("https").await;
228228
}
229229

230+
/// Lite05 Track Stream: the client resolves a track's immutable properties via
231+
/// `info()` (a TRACK request answered with TRACK_INFO) without subscribing, then
232+
/// subscribes and reads a frame. Exercises the on-demand info path end-to-end.
233+
async fn lite05_info_roundtrip(scheme: &str) {
234+
use moq_native::moq_net::Timescale;
235+
236+
let pub_origin = Origin::random().produce();
237+
let mut broadcast = pub_origin.create_broadcast("test").expect("create broadcast");
238+
let mut track = broadcast
239+
.create_track(Track::new("video").with_timescale(Timescale::MICRO))
240+
.expect("create track");
241+
242+
let mut group = track.append_group().expect("append group");
243+
let frame = moq_native::moq_net::Frame {
244+
size: 5,
245+
timestamp: Some(moq_native::moq_net::Timestamp::new(10_000, Timescale::MICRO).unwrap()),
246+
};
247+
let mut writer = group.create_frame(frame).expect("create frame");
248+
writer.write(bytes::Bytes::from_static(b"hello")).expect("write frame");
249+
writer.finish().expect("finish frame");
250+
group.finish().expect("finish group");
251+
252+
let mut server_config = moq_native::ServerConfig::default();
253+
server_config.bind = Some("[::]:0".to_string());
254+
server_config.tls.generate = vec!["localhost".into()];
255+
server_config.version = vec!["moq-lite-05-wip".parse().unwrap()];
256+
let mut server = server_config.init().expect("init server");
257+
let addr = server.local_addr().expect("local addr");
258+
259+
let sub_origin = Origin::random().produce();
260+
let mut announcements = sub_origin.consume().announced();
261+
262+
let mut client_config = moq_native::ClientConfig::default();
263+
client_config.tls.disable_verify = Some(true);
264+
client_config.version = vec!["moq-lite-05-wip".parse().unwrap()];
265+
let client = client_config.init().expect("init client");
266+
let url: url::Url = format!("{scheme}://localhost:{}", addr.port()).parse().unwrap();
267+
268+
let server_handle = tokio::spawn(async move {
269+
let request = server.accept().await.expect("no incoming connection");
270+
let session = request.with_publisher(pub_origin.clone()).ok().await?;
271+
let _broadcast = broadcast;
272+
let _track = track;
273+
let _ = session.closed().await;
274+
Ok::<_, anyhow::Error>(())
275+
});
276+
277+
let client = client.with_consumer(sub_origin);
278+
let session = tokio::time::timeout(TIMEOUT, client.connect(url))
279+
.await
280+
.expect("client connect timed out")
281+
.expect("client connect failed");
282+
283+
let (path, bc) = tokio::time::timeout(TIMEOUT, announcements.next())
284+
.await
285+
.expect("announce timed out")
286+
.expect("origin closed");
287+
assert_eq!(path.as_str(), "test");
288+
let bc = bc.broadcast().expect("expected announce, got unannounce");
289+
290+
// Resolve the track's immutable info without subscribing.
291+
let info = tokio::time::timeout(TIMEOUT, bc.consume_track("video").info())
292+
.await
293+
.expect("info timed out")
294+
.expect("info failed");
295+
assert_eq!(info.timescale, Some(Timescale::MICRO));
296+
297+
// A subscribe still works (and reuses the now-cached info).
298+
let mut track_sub = bc
299+
.consume_track("video")
300+
.subscribe(None)
301+
.await
302+
.expect("subscribe failed");
303+
let mut group_sub = tokio::time::timeout(TIMEOUT, track_sub.recv_group())
304+
.await
305+
.expect("recv_group timed out")
306+
.expect("recv_group failed")
307+
.expect("track closed prematurely");
308+
let frame = tokio::time::timeout(TIMEOUT, group_sub.read_frame())
309+
.await
310+
.expect("read_frame timed out")
311+
.expect("read_frame failed")
312+
.expect("group closed prematurely");
313+
assert_eq!(&*frame, b"hello");
314+
315+
drop(session);
316+
server_handle
317+
.await
318+
.expect("server task panicked")
319+
.expect("server task failed");
320+
}
321+
322+
#[tracing_test::traced_test]
323+
#[tokio::test]
324+
async fn broadcast_moq_lite_05_info_webtransport() {
325+
lite05_info_roundtrip("https").await;
326+
}
327+
230328
/// On Lite05 a publisher that doesn't advertise a timescale still works:
231329
/// SUBSCRIBE_OK carries `timescale = 0` and neither side encodes a
232330
/// per-frame timestamp byte. Subscribers receive `frame.timestamp = None`.

0 commit comments

Comments
 (0)