Skip to content

Commit

Permalink
Fix tests and async use
Browse files Browse the repository at this point in the history
- fix import order (tests fail when helper files
import qasync before pyqt6)
- clean up some run_until completes
- minor test fixes
  • Loading branch information
marmarta committed Nov 1, 2024
1 parent d21b22a commit c29be64
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 79 deletions.
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ ignore=tests,
ui_restoredlg.py,
ui_settingsdlg.py,
resources.py
extension-pkg-whitelist=PyQt5
extension-pkg-whitelist=PyQt6

[MESSAGES CONTROL]
disable=
Expand Down
2 changes: 1 addition & 1 deletion debian/rules
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

include /usr/share/dpkg/default.mk
export DESTDIR=$(shell pwd)/debian/tmp
export QT_SELECT=qt5
export QT_SELECT=qt6

%:
dh $@ --with python3 --buildsystem=pybuild
Expand Down
2 changes: 1 addition & 1 deletion qubesmanager/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import asyncio
import sys

import qasync
from PyQt6 import QtWidgets
import qasync

qtapp = None
loop = None
Expand Down
16 changes: 8 additions & 8 deletions qubesmanager/tests/test_backup.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,9 +486,9 @@ def test_25_successful_backup(self, _a, _b, mock_remove,
self.dialog.backup_finished()

self.assertFalse(self.dialog.button(
self.dialog.CancelButton).isEnabled())
QtWidgets.QWizard.WizardButton.CancelButton).isEnabled())
self.assertTrue(self.dialog.button(
self.dialog.FinishButton).isEnabled())
QtWidgets.QWizard.WizardButton.FinishButton).isEnabled())
mock_remove.assert_called_once_with(
'/etc/qubes/backup/qubes-manager-backup-tmp.conf')
self.assertEqual(mock_system.call_count, 0,
Expand Down Expand Up @@ -526,9 +526,9 @@ def test_26_success_backup_poweroff(
self.dialog.backup_finished()

self.assertFalse(self.dialog.button(
self.dialog.CancelButton).isEnabled())
QtWidgets.QWizard.WizardButton.CancelButton).isEnabled())
self.assertTrue(self.dialog.button(
self.dialog.FinishButton).isEnabled())
QtWidgets.QWizard.WizardButton.FinishButton).isEnabled())
mock_remove.assert_called_once_with(
'/etc/qubes/backup/qubes-manager-backup-tmp.conf')
mock_system.assert_called_once_with('systemctl poweroff')
Expand Down Expand Up @@ -564,9 +564,9 @@ def test_27_failed_backup(
self.dialog.backup_finished()

self.assertFalse(self.dialog.button(
self.dialog.CancelButton).isEnabled())
QtWidgets.QWizard.WizardButton.CancelButton).isEnabled())
self.assertTrue(self.dialog.button(
self.dialog.FinishButton).isEnabled())
QtWidgets.QWizard.WizardButton.FinishButton).isEnabled())
mock_remove.assert_called_once_with(
'/etc/qubes/backup/qubes-manager-backup-tmp.conf')
self.assertEqual(mock_system.call_count, 0,
Expand Down Expand Up @@ -596,9 +596,9 @@ def test_28_progress(

# see if backup is correctly in progress
self.assertTrue(self.dialog.button(
self.dialog.CancelButton).isEnabled())
QtWidgets.QWizard.WizardButton.CancelButton).isEnabled())
self.assertFalse(self.dialog.button(
self.dialog.FinishButton).isEnabled())
QtWidgets.QWizard.WizardButton.FinishButton).isEnabled())
self.assertEqual(self.dialog.progress_bar.value(), 0,
"Progress bar does not start at 0")

Expand Down
76 changes: 46 additions & 30 deletions qubesmanager/tests/test_create_new_vm.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ def test_02_create_simple_vm(self):
self.__click_ok()

self.mock_thread.assert_called_once_with(
self.qapp, "AppVM", "test-vm",
unittest.mock.ANY, self.qapp.default_template,
{'provides_network': False}, unittest.mock.ANY)
app=self.qapp, vmclass="AppVM", name="test-vm",
label=unittest.mock.ANY, template=self.qapp.default_template,
properties={'provides_network': False}, pool=unittest.mock.ANY)
self.mock_thread().start.assert_called_once_with()

def test_03_label(self):
Expand All @@ -80,9 +80,10 @@ def test_03_label(self):
self.__click_ok()

self.mock_thread.assert_called_once_with(
self.qapp, "AppVM", "test-vm",
self.qapp.labels['blue'], self.qapp.default_template,
unittest.mock.ANY, unittest.mock.ANY)
app=self.qapp, vmclass="AppVM", name="test-vm",
label=self.qapp.labels['blue'],
template=self.qapp.default_template,
properties=unittest.mock.ANY, pool=unittest.mock.ANY)
self.mock_thread().start.assert_called_once_with()

def test_04_template(self):
Expand All @@ -97,9 +98,10 @@ def test_04_template(self):
self.__click_ok()

self.mock_thread.assert_called_once_with(
self.qapp, "AppVM", "test-vm",
unittest.mock.ANY, template,
unittest.mock.ANY, unittest.mock.ANY)
app=self.qapp, vmclass="AppVM", name="test-vm",
label=unittest.mock.ANY,
template=template,
properties=unittest.mock.ANY, pool=unittest.mock.ANY)

def test_05_netvm(self):
netvm = None
Expand All @@ -113,9 +115,11 @@ def test_05_netvm(self):
self.__click_ok()

self.mock_thread.assert_called_once_with(
self.qapp, "AppVM", "test-vm",
unittest.mock.ANY, unittest.mock.ANY,
{'netvm': netvm, 'provides_network': False}, unittest.mock.ANY)
app=self.qapp, vmclass="AppVM", name="test-vm",
label=unittest.mock.ANY,
template=unittest.mock.ANY,
properties={'netvm': netvm, 'provides_network': False},
pool=unittest.mock.ANY)

def test_06_provides_network(self):
self.dialog.provides_network.setChecked(True)
Expand All @@ -124,9 +128,11 @@ def test_06_provides_network(self):
self.__click_ok()

self.mock_thread.assert_called_once_with(
self.qapp, "AppVM", "test-vm",
unittest.mock.ANY, unittest.mock.ANY,
{'provides_network': True}, unittest.mock.ANY)
app=self.qapp, vmclass="AppVM", name="test-vm",
label=unittest.mock.ANY,
template=unittest.mock.ANY,
properties={'provides_network': True},
pool=unittest.mock.ANY)

@unittest.mock.patch('subprocess.check_call')
def test_07_launch_settings(self, mock_call):
Expand All @@ -138,9 +144,11 @@ def test_07_launch_settings(self, mock_call):

# make sure the thread is not reporting an error
self.mock_thread.assert_called_once_with(
self.qapp, "AppVM", "test-vm",
unittest.mock.ANY, unittest.mock.ANY,
unittest.mock.ANY, unittest.mock.ANY)
app=self.qapp, vmclass="AppVM", name="test-vm",
label=unittest.mock.ANY,
template=unittest.mock.ANY,
properties=unittest.mock.ANY,
pool=unittest.mock.ANY)

self.mock_thread().msg = None
self.dialog.create_finished()
Expand All @@ -153,9 +161,11 @@ def test_08_progress_hides(self):
self.__click_ok()

self.mock_thread.assert_called_once_with(
self.qapp, "AppVM", "test-vm",
unittest.mock.ANY, unittest.mock.ANY,
unittest.mock.ANY, unittest.mock.ANY)
app=self.qapp, vmclass="AppVM", name="test-vm",
label=unittest.mock.ANY,
template=unittest.mock.ANY,
properties=unittest.mock.ANY,
pool=unittest.mock.ANY)

# make sure the thread is not reporting an error
self.mock_thread().start.assert_called_once_with()
Expand All @@ -177,9 +187,11 @@ def test_09_standalone_clone(self):

self.__click_ok()
self.mock_thread.assert_called_once_with(
self.qapp, "StandaloneVM", "test-vm",
unittest.mock.ANY, unittest.mock.ANY,
unittest.mock.ANY, unittest.mock.ANY)
app=self.qapp, vmclass="StandaloneVM", name="test-vm",
label=unittest.mock.ANY,
template=unittest.mock.ANY,
properties=unittest.mock.ANY,
pool=unittest.mock.ANY)

@unittest.mock.patch('qubesmanager.bootfromdevice.VMBootFromDeviceWindow')
@unittest.mock.patch('qubesadmin.tools.qvm_start')
Expand All @@ -197,9 +209,11 @@ def test_10_standalone_empty(self, mock_qvm_start, mock_bootwindow):

self.__click_ok()
self.mock_thread.assert_called_once_with(
self.qapp, "StandaloneVM", "test-vm",
unittest.mock.ANY, None,
unittest.mock.ANY, unittest.mock.ANY)
app=self.qapp, vmclass="StandaloneVM", name="test-vm",
label=unittest.mock.ANY,
template=None,
properties=unittest.mock.ANY,
pool=unittest.mock.ANY)

self.mock_thread().msg = None
self.dialog.create_finished()
Expand All @@ -224,9 +238,11 @@ def test_11_standalone_empty_not_install(self, mock_call):

self.__click_ok()
self.mock_thread.assert_called_once_with(
self.qapp, "StandaloneVM", "test-vm",
unittest.mock.ANY, None,
unittest.mock.ANY, unittest.mock.ANY)
app=self.qapp, vmclass="StandaloneVM", name="test-vm",
label=unittest.mock.ANY,
template=None,
properties=unittest.mock.ANY,
pool=unittest.mock.ANY)

self.mock_thread().msg = None
self.dialog.create_finished()
Expand Down
58 changes: 28 additions & 30 deletions qubesmanager/tests/test_qube_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def test_006_correct_ip_listed(self):
ip_item = self._get_table_item(row, "IP Address")
if hasattr(vm, 'ip'):
ip_value = getattr(vm, 'ip')
ip_value = "" if ip_value is None else ip_value
ip_value = "n/a" if not ip_value else ip_value
else:
ip_value = "n/a"

Expand Down Expand Up @@ -565,9 +565,8 @@ def test_222_restartvm_shutdown_meantime(self, _, mock_thread):
mock_thread().start.assert_called_once_with()

@unittest.mock.patch('qubesmanager.qube_manager.UpdateVMsThread')
def test_223_updatevm_running(self, mock_thread):
selected_vm = self._select_non_admin_vm(running=True)

def test_223_updatevm_template(self, mock_thread):
selected_vm = self._select_templatevm()
self.dialog.action_updatevm.trigger()

mock_thread.assert_called_once_with([selected_vm.name])
Expand Down Expand Up @@ -1004,7 +1003,8 @@ def test_400_event_domain_added(self):
self.addCleanup(subprocess.call, ["qvm-remove", "-f", "test-vm"])

self._run_command_and_process_events(
["qvm-create", "--label", "red", "test-vm"])
["qvm-create", "--label", "red", "test-vm"], timeout=10,
additional_timeout=5)

# a single row was added to the table
self.assertEqual(self.dialog.table.model().rowCount(), number_of_vms+1)
Expand Down Expand Up @@ -1158,6 +1158,8 @@ def test_407_prop_change_netvm(self):
vm_row = self._find_vm_row(target_vm_name)

old_netvm = self._get_table_item(vm_row, "NetVM")
# in case of "default (...)" take "default"
old_newvm = old_netvm.split(' ')[0]
new_netvm = None
for vm in self.qapp.domains:
if getattr(vm, "provides_network", False) and vm.name != old_netvm:
Expand Down Expand Up @@ -1434,33 +1436,29 @@ def _run_command_and_process_events(self, command, timeout=5,
"""
asyncio.set_event_loop(self.loop)

future1 = asyncio.ensure_future(self.dispatcher.listen_for_events())
self.loop.run_until_complete(asyncio.sleep(0))

future2 = asyncio.create_subprocess_exec(*command,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)

future2 = self.loop.run_until_complete(future2).wait()

if additional_timeout:
(done, pending) = self.loop.run_until_complete(
asyncio.wait({future1,
asyncio.create_task(future2)}, timeout=timeout,
return_when=asyncio.FIRST_COMPLETED))
(done, pending) = self.loop.run_until_complete(
asyncio.wait(pending, timeout=additional_timeout))
else:
(done, pending) = self.loop.run_until_complete(
asyncio.wait({future1,
asyncio.create_task(future2)}, timeout=timeout))
async def do_tasks():
coro = asyncio.create_subprocess_exec(*command,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)

tasks = {asyncio.create_task(self.dispatcher.listen_for_events()),
asyncio.create_task(coro)}

if additional_timeout:
(done, pending) = await asyncio.wait(
tasks,
timeout=timeout,
return_when=asyncio.FIRST_COMPLETED)
(done, pending) = await asyncio.wait(
pending, timeout=additional_timeout)
else:
(done, pending) = await asyncio.wait(tasks, timeout=timeout)

for task in pending:
with contextlib.suppress(asyncio.CancelledError):
task.cancel()
for task in pending:
with contextlib.suppress(asyncio.CancelledError):
task.cancel()

self.loop.call_soon(self.loop.stop)
self.loop.run_forever()
self.loop.run_until_complete(do_tasks())

def _create_set_of_current_vms(self):
result = set()
Expand Down
19 changes: 11 additions & 8 deletions qubesmanager/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@
import asyncio
from contextlib import suppress
import sys
import qasync
from qubesadmin import events
from qubesadmin import exc
import xdg.BaseDirectory
import pathlib
import shutil

from PyQt6 import QtWidgets, QtCore, QtGui # pylint: disable=import-error
import qasync


# important usage note: which initialize_widget should I use?
Expand Down Expand Up @@ -547,18 +547,21 @@ def run_asynchronous(window_class):

loop = qasync.QEventLoop(qt_app)
asyncio.set_event_loop(loop)
dispatcher = events.EventsDispatcher(qubes_app)

window = window_class(qt_app, qubes_app, dispatcher)
async def setup():
dispatcher = events.EventsDispatcher(qubes_app)

if hasattr(window, "setup_application"):
window.setup_application()
window = window_class(qt_app, qubes_app, dispatcher)

window.show()
if hasattr(window, "setup_application"):
window.setup_application()

window.show()

await dispatcher.listen_for_events()

try:
loop.run_until_complete(
asyncio.ensure_future(dispatcher.listen_for_events()))
loop.run_until_complete(asyncio.ensure_future(setup()))
except asyncio.CancelledError:
pass
except Exception: # pylint: disable=broad-except
Expand Down

0 comments on commit c29be64

Please sign in to comment.