@@ -415,4 +415,112 @@ mod tests {
415415 assert ! ( command_queue. pop( ) . is_none( ) ) ;
416416 return ;
417417 }
418+
419+ /// `SoundInstance` MUST enqueue commands when it is the active instance.
420+ #[ test]
421+ fn sound_instance_enqueues_commands_when_active ( ) {
422+ let command_queue: Arc < PlaybackCommandQueue > =
423+ Arc :: new ( CommandQueue :: new ( ) ) ;
424+ let shared_state = Arc :: new ( PlaybackSharedState :: new ( ) ) ;
425+
426+ shared_state. set_active_instance_id ( 7 ) ;
427+ shared_state. set_state ( PlaybackState :: Stopped ) ;
428+
429+ let mut instance = SoundInstance {
430+ instance_id : 7 ,
431+ command_queue : command_queue. clone ( ) ,
432+ shared_state : shared_state. clone ( ) ,
433+ } ;
434+
435+ instance. play ( ) ;
436+ instance. pause ( ) ;
437+ instance. set_looping ( true ) ;
438+ instance. stop ( ) ;
439+
440+ assert ! ( matches!(
441+ command_queue. pop( ) ,
442+ Some ( PlaybackCommand :: Play { instance_id: 7 } )
443+ ) ) ;
444+ assert ! ( matches!(
445+ command_queue. pop( ) ,
446+ Some ( PlaybackCommand :: Pause { instance_id: 7 } )
447+ ) ) ;
448+ assert ! ( matches!(
449+ command_queue. pop( ) ,
450+ Some ( PlaybackCommand :: SetLooping {
451+ instance_id: 7 ,
452+ looping: true
453+ } )
454+ ) ) ;
455+ assert ! ( matches!(
456+ command_queue. pop( ) ,
457+ Some ( PlaybackCommand :: Stop { instance_id: 7 } )
458+ ) ) ;
459+ assert ! ( command_queue. pop( ) . is_none( ) ) ;
460+ return ;
461+ }
462+
463+ /// `SoundInstance` state queries MUST reflect the shared state when active.
464+ #[ test]
465+ fn sound_instance_state_follows_shared_state_when_active ( ) {
466+ let command_queue: Arc < PlaybackCommandQueue > =
467+ Arc :: new ( CommandQueue :: new ( ) ) ;
468+ let shared_state = Arc :: new ( PlaybackSharedState :: new ( ) ) ;
469+
470+ shared_state. set_active_instance_id ( 1 ) ;
471+
472+ let instance = SoundInstance {
473+ instance_id : 1 ,
474+ command_queue,
475+ shared_state : shared_state. clone ( ) ,
476+ } ;
477+
478+ shared_state. set_state ( PlaybackState :: Stopped ) ;
479+ assert_eq ! ( instance. state( ) , PlaybackState :: Stopped ) ;
480+ assert ! ( instance. is_stopped( ) ) ;
481+ assert ! ( !instance. is_playing( ) ) ;
482+ assert ! ( !instance. is_paused( ) ) ;
483+
484+ shared_state. set_state ( PlaybackState :: Playing ) ;
485+ assert_eq ! ( instance. state( ) , PlaybackState :: Playing ) ;
486+ assert ! ( instance. is_playing( ) ) ;
487+ assert ! ( !instance. is_paused( ) ) ;
488+ assert ! ( !instance. is_stopped( ) ) ;
489+
490+ shared_state. set_state ( PlaybackState :: Paused ) ;
491+ assert_eq ! ( instance. state( ) , PlaybackState :: Paused ) ;
492+ assert ! ( !instance. is_playing( ) ) ;
493+ assert ! ( instance. is_paused( ) ) ;
494+ assert ! ( !instance. is_stopped( ) ) ;
495+ return ;
496+ }
497+
498+ /// The builder MUST reject unsupported channel counts before device init.
499+ #[ test]
500+ fn audio_context_builder_rejects_too_many_channels ( ) {
501+ let result = AudioContextBuilder :: new ( )
502+ . with_channels ( ( MAX_PLAYBACK_CHANNELS + 1 ) as u16 )
503+ . build ( ) ;
504+
505+ assert ! ( matches!(
506+ result,
507+ Err ( AudioError :: InvalidChannels { requested } )
508+ if requested == ( MAX_PLAYBACK_CHANNELS + 1 ) as u16
509+ ) ) ;
510+ return ;
511+ }
512+
513+ /// Builder configuration MUST store requested fields.
514+ #[ test]
515+ fn audio_context_builder_stores_configuration ( ) {
516+ let builder = AudioContextBuilder :: new ( )
517+ . with_sample_rate ( 48_000 )
518+ . with_channels ( 2 )
519+ . with_label ( "test-context" ) ;
520+
521+ assert_eq ! ( builder. sample_rate, Some ( 48_000 ) ) ;
522+ assert_eq ! ( builder. channels, Some ( 2 ) ) ;
523+ assert_eq ! ( builder. label. as_deref( ) , Some ( "test-context" ) ) ;
524+ return ;
525+ }
418526}
0 commit comments