@@ -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