From 5c56582197693734c82c3a583eaf39113c252a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Thu, 3 Apr 2025 02:35:01 +0200 Subject: [PATCH 1/3] tests: avoid cursor blinking in test_300_bug_1028_gui_memory_pinning In Whonix, setup wizard shows up shortly after startup. When that happens just between capturing screenshot in dom0 and VM, xterm will loose focus, causing different cursor and thus screenshots not matching. Avoid this issue by disabling whonix setup wizard by pretending it was completed already. --- qubes/tests/integ/vm_qrexec_gui.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qubes/tests/integ/vm_qrexec_gui.py b/qubes/tests/integ/vm_qrexec_gui.py index febf825e1..1c133ca32 100644 --- a/qubes/tests/integ/vm_qrexec_gui.py +++ b/qubes/tests/integ/vm_qrexec_gui.py @@ -692,6 +692,12 @@ async def _test_300_bug_1028_gui_memory_pinning(self): # prevent Whonix startup notifications from interfering self.testvm1.features["service.whonix-tor-disable"] = True await self.testvm1.start() + # avoid whonix wizard showing up + if "whonix" in self.template: + await self.testvm1.run_for_stdio( + "touch /var/lib/whonix/do_once/whonixsetup.done", + user="root", + ) await self.wait_for_session(self.testvm1) # and allow large map count From ce0e51288e144e943eda4a9b2664bc7d4979b7b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Fri, 4 Apr 2025 03:51:28 +0200 Subject: [PATCH 2/3] tests: print stderr of any CalledProcessError exception Generally most tests should do that, but there are few exceptions, so deal with it in a more generic way. At least for now. --- qubes/tests/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qubes/tests/__init__.py b/qubes/tests/__init__.py index d0d1dc35d..48b48da42 100644 --- a/qubes/tests/__init__.py +++ b/qubes/tests/__init__.py @@ -440,6 +440,9 @@ def __exit__(self, exc_type, exc_val, exc_tb): return ex = exc_val while ex is not None: + if isinstance(ex, subprocess.CalledProcessError): + # include stderr in the log + print(f"CalledProcessError({ex.cmd}): stderr={ex.stderr!r}") if isinstance(ex, qubes.exc.QubesVMError): ex.vm = None traceback.clear_frames(ex.__traceback__) From 20aa5490396bda016ec8e63581a97d8d38860414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Sun, 6 Apr 2025 01:13:29 +0200 Subject: [PATCH 3/3] tests: adjust pipewire test for new --raw option pipewire-utils in Fedora 42 no longer plays raw audio with --format etc options unless --raw is added too. But older pipewire-utils doesn't have the --raw option, so it must be added only when it's actually supported. Furthermore, --raw option ignores the file name argument (but it's still mandatory) and always uses stdin/out. Change pw-record call to always use stdout (by using '-' file name) and redirection, so it works in both cases. QubesOS/qubes-issues#9807 --- qubes/tests/integ/audio.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/qubes/tests/integ/audio.py b/qubes/tests/integ/audio.py index 4c1fcbf39..ab650380d 100644 --- a/qubes/tests/integ/audio.py +++ b/qubes/tests/integ/audio.py @@ -65,6 +65,7 @@ def wait_for_pulseaudio_startup(self, vm): self.loop.run_until_complete(asyncio.sleep(1)) def prepare_audio_test(self, backend): + self.pwplay_opts = "" self.loop.run_until_complete(self.testvm1.start()) pulseaudio_units = "pulseaudio.socket pulseaudio.service" pipewire_units = "pipewire.socket wireplumber.service pipewire.service" @@ -78,6 +79,15 @@ def prepare_audio_test(self, backend): ): self.skipTest("PipeWire audio not supported in Debian 11") self.testvm1.features["service.pipewire"] = True + try: + self.loop.run_until_complete( + self.testvm1.run_for_stdio( + "pw-play --help | grep -q -- --raw", + ) + ) + self.pwplay_opts = "--raw" + except subprocess.CalledProcessError: + pass elif backend == "pulseaudio": # Use PulseAudio if it is installed. If it is not installed, # PipeWire will still run, and its PulseAudio emulation will @@ -210,7 +220,10 @@ def common_audio_playback(self): ) local_user = grp.getgrnam("qubes").gr_mem[0] if self.testvm1.features["service.pipewire"]: - cmd = "timeout 20s pw-play --format=f32 --rate=44100 --channels=1 - < audio_in.snd" + cmd = ( + f"timeout 20s pw-play {self.pwplay_opts} --format=f32 " + f"--rate=44100 --channels=1 - < audio_in.snd" + ) else: cmd = ( "timeout 20s paplay --format=float32le --rate=44100 --channels=1 " @@ -384,11 +397,10 @@ def common_audio_record_muted(self): audio_in = b"\x20" * 4 * 44100 local_user = grp.getgrnam("qubes").gr_mem[0] sudo = ["sudo", "-E", "-u", local_user] - # Need to use .snd extension so that pw-play (really libsndfile) - # recognizes the file as raw audio. if self.testvm1.features["service.pipewire"]: cmd = ( - "pw-record --format=f32 --rate=44100 --channels=1 audio_rec.snd" + f"pw-record {self.pwplay_opts} --format=f32 --rate=44100 " + f"--channels=1 - > audio_rec.snd" ) kill_cmd = "pkill --signal SIGINT pw-record" else: @@ -448,12 +460,10 @@ def common_audio_record_unmuted(self, attach_mic=True, detach_mic=True): local_user = grp.getgrnam("qubes").gr_mem[0] sudo = ["sudo", "-E", "-u", local_user] - # Need to use .snd extension so that pw-play (really libsndfile) - # recognizes the file as raw audio. if self.testvm1.features["service.pipewire"]: record_cmd = ( - "pw-record --format=f32 --rate=44100 --channels=1 " - "audio_rec.snd" + f"pw-record {self.pwplay_opts} --format=f32 --rate=44100 " + "--channels=1 - > audio_rec.snd" ) kill_cmd = "pkill --signal SIGINT pw-record" else: