@@ -125,6 +125,8 @@ def create_audio_vm(self, backend, start=True):
125125admin.vm.property.Get +is_preload {vm} @tag:audiovm-{vm} allow target=dom0
126126admin.vm.feature.CheckWithTemplate +audio {vm} @tag:audiovm-{vm} allow target=dom0
127127admin.vm.feature.CheckWithTemplate +audio-model {vm} @tag:audiovm-{vm} allow target=dom0
128+ admin.vm.feature.CheckWithTemplate +audio-initial-volume {vm} @tag:audiovm-{vm} allow target=dom0
129+ admin.vm.feature.CheckWithTemplate +audio-initial-volume {vm} {vm} allow target=dom0
128130""" .format (
129131 vm = self .audiovm .name
130132 )
@@ -368,6 +370,32 @@ def _configure_audio_recording(self, vm, expect_stream=True):
368370
369371 self .assertGreater (attempts_left , 0 , "Failed to move-source-output" )
370372
373+ def _get_sink_volume (self , vm ) -> str :
374+ """Return VM's sink-input volume percent or `mute` if mute"""
375+ audiovm = vm .audiovm
376+
377+ sinks = json .loads (
378+ self ._call_in_audiovm (
379+ audiovm , ["pactl" , "-f" , "json" , "list" , "sink-inputs" ]
380+ )
381+ )
382+
383+ if not sinks :
384+ self .fail ("no sink-inputs found in {}" .format (audiovm .name ))
385+ assert False
386+
387+ for sink in sinks :
388+ if sink ["properties" ]["application.name" ] == vm .name :
389+ if sink ["mute" ] == "True" :
390+ return "mute"
391+ vol_l = sink ["volume" ]["front-left" ]["value_percent" ]
392+ vol_r = sink ["volume" ]["front-right" ]["value_percent" ]
393+ assert vol_l == vol_r
394+ return vol_l
395+
396+ self .fail ("{} sink-input not found in {}" .format (vm .name , audiovm .name ))
397+ assert False
398+
371399 async def retrieve_audio_input (self , vm , status ):
372400 try :
373401 await asyncio .wait_for (
@@ -748,6 +776,28 @@ def test_260_audio_mic_enabled_switch_audiovm(self):
748776 self .assert_pacat_running (self .audiovm , self .testvm1 , True )
749777 self .common_audio_record_muted ()
750778
779+ @unittest .skipUnless (
780+ spawn .find_executable ("pactl" ),
781+ "pulseaudio-utils not installed in dom0" ,
782+ )
783+ def test_261_audio_initial_volume (self ):
784+ self .loop .run_until_complete (self .testvm1 .start ())
785+ self .wait_for_pulseaudio_startup (self .testvm1 )
786+ assert self ._get_sink_volume (vm ) == "100%"
787+ self .loop .run_until_complete (self .testvm1 .shutdown ())
788+
789+ self .testvm1 .features ["audio-initial-volume" ] = "42"
790+ self .loop .run_until_complete (self .testvm1 .start ())
791+ self .wait_for_pulseaudio_startup (self .testvm1 )
792+ assert self ._get_sink_volume (vm ) == "42%"
793+ self .loop .run_until_complete (self .testvm1 .shutdown ())
794+
795+ self .testvm1 .features ["audio-initial-volume" ] = "mute"
796+ self .loop .run_until_complete (self .testvm1 .start ())
797+ self .wait_for_pulseaudio_startup (self .testvm1 )
798+ assert self ._get_sink_volume (vm ) == "mute"
799+ self .loop .run_until_complete (self .testvm1 .shutdown ())
800+
751801
752802def create_testcases_for_templates ():
753803 yield from qubes .tests .create_testcases_for_templates (
0 commit comments