From 3815e0b5cfeb228bf466c2533134551b17bf5dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Thu, 6 Aug 2020 03:27:40 +0200 Subject: [PATCH 1/5] tests: make qvm-sync-clock test more reliable Compare the time with the "current" time retrieved from ClockVM just before comparing, not with the test start time. This should work even if the test machine is quite slow (test taking more than 30s). --- qubes/tests/integ/vm_qrexec_gui.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/qubes/tests/integ/vm_qrexec_gui.py b/qubes/tests/integ/vm_qrexec_gui.py index dba1fccb..28cab2db 100644 --- a/qubes/tests/integ/vm_qrexec_gui.py +++ b/qubes/tests/integ/vm_qrexec_gui.py @@ -399,10 +399,13 @@ class TC_00_AppVMMixin(object): self.assertEqual(p.returncode, 0) vm_time, _ = self.loop.run_until_complete( self.testvm2.run_for_stdio('date -u +%s')) - self.assertAlmostEquals(int(vm_time), int(start_time), delta=30) + # get current time + current_time, _ = self.loop.run_until_complete( + self.testvm1.run_for_stdio('date -u +%s')) + self.assertAlmostEquals(int(vm_time), int(current_time), delta=30) dom0_time = subprocess.check_output(['date', '-u', '+%s']) - self.assertAlmostEquals(int(dom0_time), int(start_time), delta=30) + self.assertAlmostEquals(int(dom0_time), int(current_time), delta=30) except: # reset time to some approximation of the real time From 1abf949fafa37e3dc4e73403e5078e22e99c516a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Fri, 7 Aug 2020 01:55:10 +0200 Subject: [PATCH 2/5] tests: fix audio recording test To calculate frequency it needs to use samples per second (44100), not samples pre recording lenght. This caused shorter recordings to not fit into the margin. --- qubes/tests/integ/vm_qrexec_gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qubes/tests/integ/vm_qrexec_gui.py b/qubes/tests/integ/vm_qrexec_gui.py index 28cab2db..4d20ffb5 100644 --- a/qubes/tests/integ/vm_qrexec_gui.py +++ b/qubes/tests/integ/vm_qrexec_gui.py @@ -458,7 +458,7 @@ class TC_00_AppVMMixin(object): (rec[:-1] < -threshold))[0] np.seterr('raise') # compare against sine wave frequency - rec_freq = rec_size/np.mean(np.diff(crossings)) + rec_freq = 44100/np.mean(np.diff(crossings)) if not sfreq*0.8 < rec_freq < sfreq*1.2: self.fail('frequency {} not in specified range' .format(rec_freq)) From 8b076dfe5fb179fd45a144540bb8dace8ba6ae38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Sat, 8 Aug 2020 19:22:26 +0200 Subject: [PATCH 3/5] tests: workaround a race in qrexec test qrexec-client-vm may return earlier than it's child process (it exits right away, without waiting for its child). Add a small wait before reading exit code from a file. --- qubes/tests/integ/qrexec.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qubes/tests/integ/qrexec.py b/qubes/tests/integ/qrexec.py index b357f5f6..2776e3de 100644 --- a/qubes/tests/integ/qrexec.py +++ b/qubes/tests/integ/qrexec.py @@ -201,6 +201,7 @@ class TC_00_QrexecMixin(object): self.testvm1.run_for_stdio('''\ /usr/lib/qubes/qrexec-client-vm dom0 test.Abort \ /bin/sh -c 'cat /dev/zero; echo $? >/tmp/exit-code'; + sleep 1; e=$(cat /tmp/exit-code); test $e -eq 141 -o $e -eq 1'''), timeout=10)) From 46cc4ca91098d1f48eb7529cbadffbffe77d8f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Sat, 8 Aug 2020 20:32:51 +0200 Subject: [PATCH 4/5] tests: collect detailed diagnostics on failure Help debugging test failures by collecting detailed information on failure. It will be logger to the standard logger, which will end up either on stderr or in journalctl. --- qubes/tests/integ/network.py | 28 +++++++++++++++++++++++++++- qubes/tests/integ/network_ipv6.py | 13 +++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/qubes/tests/integ/network.py b/qubes/tests/integ/network.py index faf7371f..f9c28bc9 100644 --- a/qubes/tests/integ/network.py +++ b/qubes/tests/integ/network.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, see . # - +import contextlib from distutils import spawn import asyncio @@ -87,6 +87,32 @@ class VmNetworkingMixin(object): self.configure_netvm() + def _run_cmd_and_log_output(self, vm, cmd): + """Used in tearDown to collect more info""" + if not vm.is_running(): + return + with contextlib.suppress(subprocess.CalledProcessError): + output = self.loop.run_until_complete( + self.testnetvm.run_for_stdio(cmd, user='root')) + self.log.error('{}: {}: {}'.format(vm.name, cmd, output)) + + def tearDown(self): + # collect more info on failure + if self._outcome and not self._outcome.success: + for vm in (self.testnetvm, self.testvm1, getattr(self, 'proxy', None)): + if vm is None: + continue + self._run_cmd_and_log_output(vm, 'ip a') + self._run_cmd_and_log_output(vm, 'ip r') + self._run_cmd_and_log_output(vm, 'iptables -vnL') + self._run_cmd_and_log_output(vm, 'iptables -vnL -t nat') + self._run_cmd_and_log_output(vm, 'nft list table qubes-firewall') + self._run_cmd_and_log_output(vm, 'systemctl --no-pager status qubes-firewall') + self._run_cmd_and_log_output(vm, 'systemctl --no-pager status qubes-iptables') + self._run_cmd_and_log_output(vm, 'systemctl --no-pager status xendriverdomain') + + super(VmNetworkingMixin, self).tearDown() + def configure_netvm(self): ''' diff --git a/qubes/tests/integ/network_ipv6.py b/qubes/tests/integ/network_ipv6.py index ac9db460..702e9e64 100644 --- a/qubes/tests/integ/network_ipv6.py +++ b/qubes/tests/integ/network_ipv6.py @@ -43,6 +43,19 @@ class VmIPv6NetworkingMixin(VmNetworkingMixin): self.ping6_ip = self.ping6_cmd.format(target=self.test_ip6) self.ping6_name = self.ping6_cmd.format(target=self.test_name) + def tearDown(self): + # collect more info on failure (ipv4 info collected in parent) + if self._outcome and not self._outcome.success: + for vm in (self.testnetvm, self.testvm1, getattr(self, 'proxy', None)): + if vm is None: + continue + self._run_cmd_and_log_output(vm, 'ip -6 r') + self._run_cmd_and_log_output(vm, 'ip6tables -vnL') + self._run_cmd_and_log_output(vm, 'ip6tables -vnL -t nat') + self._run_cmd_and_log_output(vm, 'nft list table ip6 qubes-firewall') + + super().tearDown() + def configure_netvm(self): ''' :type self: qubes.tests.SystemTestCase | VmIPv6NetworkingMixin From c425df6c57a51513a33cd50710fd1234ae5d9575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Sat, 8 Aug 2020 21:55:45 +0200 Subject: [PATCH 5/5] tests/extra: add vm.run(..., gui=) argument A convenient (and compatible) option to wait for user session before starting the command. --- qubes/tests/extra.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/qubes/tests/extra.py b/qubes/tests/extra.py index a25f0850..92f21969 100644 --- a/qubes/tests/extra.py +++ b/qubes/tests/extra.py @@ -80,7 +80,14 @@ class VMWrapper(object): return self._loop.run_until_complete(self._vm.shutdown()) def run(self, command, wait=False, user=None, passio_popen=False, - passio_stderr=False, **kwargs): + passio_stderr=False, gui=False, **kwargs): + if gui: + try: + self._loop.run_until_complete( + self._vm.run_service_for_stdio('qubes.WaitForSession', + user=user)) + except subprocess.CalledProcessError as err: + return err.returncode if wait: try: self._loop.run_until_complete(