Browse Source

Fixed manager tests to use Qt5

Also due to old workarounds not working around anymore,
switched over to "single QApplication object" model.
Marta Marczykowska-Górecka 4 years ago
parent
commit
6f73ef2163

+ 16 - 0
qubesmanager/tests/__init__.py

@@ -0,0 +1,16 @@
+import asyncio
+import sys
+
+import quamash
+from PyQt5 import QtWidgets
+
+qtapp = None
+loop = None
+
+def init_qtapp():
+    global qtapp, loop
+    if qtapp is None:
+        qtapp = QtWidgets.QApplication(sys.argv)
+        loop = quamash.QEventLoop(qtapp)
+        asyncio.set_event_loop(loop)
+    return qtapp, loop

+ 19 - 46
qubesmanager/tests/test_backup.py

@@ -20,21 +20,20 @@
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 #
 import logging.handlers
-import sys
 import unittest
 import unittest.mock
 
-from PyQt4 import QtGui, QtTest, QtCore
+from PyQt5 import QtTest, QtCore, QtWidgets
 from qubesadmin import Qubes, events, utils, exc
 from qubesmanager import backup
-import quamash
+from qubesmanager.tests import init_qtapp
 import asyncio
-import gc
 
 
 class BackupTest(unittest.TestCase):
     def setUp(self):
         super(BackupTest, self).setUp()
+        self.qtapp, self.loop = init_qtapp()
 
         # mock up nonexistence of saved backup settings
         self.patcher_open = unittest.mock.patch('builtins.open')
@@ -49,43 +48,16 @@ class BackupTest(unittest.TestCase):
         self.addCleanup(self.patcher_thread.stop)
 
         self.qapp = Qubes()
-        self.qtapp = QtGui.QApplication(["test", "-style", "cleanlooks"])
-
         self.dispatcher = events.EventsDispatcher(self.qapp)
 
-        self.loop = quamash.QEventLoop(self.qtapp)
-
         self.dialog = backup.BackupVMsWindow(
             self.qtapp, self.qapp, self.dispatcher)
-
         self.dialog.show()
 
     def tearDown(self):
-        self.dialog.hide()
-        # process any pending events before destroying the object
-        self.qtapp.processEvents()
-
-        # queue destroying the QApplication object, do that for any other QT
-        # related objects here too
-        self.qtapp.deleteLater()
-        self.dialog.deleteLater()
-
-        # process any pending events (other than just queued destroy),
-        # just in case
+        self.dialog.close()
         self.qtapp.processEvents()
-
-        # execute main loop, which will process all events, _
-        # including just queued destroy_
-        self.loop.run_until_complete(asyncio.sleep(0))
-
-        # at this point it QT objects are destroyed, cleanup all remaining
-        # references;
-        # del other QT object here too
-        self.loop.close()
-        del self.dialog
-        del self.qtapp
-        del self.loop
-        gc.collect()
+        yield from asyncio.sleep(1)
         super(BackupTest, self).tearDown()
 
     def test_00_window_loads(self):
@@ -106,16 +78,17 @@ class BackupTest(unittest.TestCase):
                         "Compress backup should be checked by default")
 
         # correct VMs are selected
-        include_in_backups_no = len([vm for vm in self.qapp.domains
-                                     if not vm.features.get('internal', False)
-                                     and getattr(vm, 'include_in_backups', True)])
+        include_in_backups_no = len(
+            [vm for vm in self.qapp.domains
+             if not vm.features.get('internal', False)
+             and getattr(vm, 'include_in_backups', True)])
         selected_no = self.dialog.select_vms_widget.selected_list.count()
         self.assertEqual(include_in_backups_no, selected_no,
                          "Incorrect VMs selected by default")
 
         # passphrase is empty
         self.assertEqual(self.dialog.passphrase_line_edit.text(), "",
-                          "Passphrase should be empty")
+                         "Passphrase should be empty")
 
         # save defaults
         self.assertTrue(self.dialog.save_profile_checkbox.isChecked(),
@@ -399,7 +372,7 @@ class BackupTest(unittest.TestCase):
         self.assertTrue(self.dialog.unrecognized_config_label.isVisible())
 
     @unittest.mock.patch('qubesmanager.backup_utils.load_backup_profile')
-    @unittest.mock.patch('PyQt4.QtGui.QMessageBox.information')
+    @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.information')
     def test_22_loading_settings_exc(self, mock_info, mock_load):
 
         mock_load.side_effect = exc.QubesException('Error')
@@ -436,7 +409,7 @@ class BackupTest(unittest.TestCase):
             mock_remove.assert_called_once_with(
                 '/etc/qubes/backup/qubes-manager-backup-tmp.conf')
 
-    @unittest.mock.patch('PyQt4.QtGui.QMessageBox.warning')
+    @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning')
     @unittest.mock.patch('qubesmanager.backup_utils.write_backup_profile')
     @unittest.mock.patch('qubesadmin.Qubes.qubesd_call',
                          return_value=b'backup output')
@@ -461,7 +434,7 @@ class BackupTest(unittest.TestCase):
             mock_remove.assert_called_once_with(
                 '/etc/qubes/backup/qubes-manager-backup-tmp.conf')
 
-    @unittest.mock.patch('PyQt4.QtGui.QMessageBox.warning')
+    @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning')
     @unittest.mock.patch('os.system')
     @unittest.mock.patch('os.remove')
     @unittest.mock.patch('qubesmanager.backup_utils.write_backup_profile')
@@ -501,7 +474,7 @@ class BackupTest(unittest.TestCase):
         self.assertEqual(mock_warning.call_count, 0,
                          "Backup succeeded but received warning")
 
-    @unittest.mock.patch('PyQt4.QtGui.QMessageBox.warning')
+    @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning')
     @unittest.mock.patch('os.system')
     @unittest.mock.patch('os.remove')
     @unittest.mock.patch('qubesmanager.backup_utils.write_backup_profile')
@@ -540,7 +513,7 @@ class BackupTest(unittest.TestCase):
         self.assertEqual(mock_warning.call_count, 0,
                          "Backup succeeded but received warning")
 
-    @unittest.mock.patch('PyQt4.QtGui.QMessageBox.warning')
+    @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning')
     @unittest.mock.patch('os.system')
     @unittest.mock.patch('os.remove')
     @unittest.mock.patch('qubesmanager.backup_utils.write_backup_profile')
@@ -578,14 +551,14 @@ class BackupTest(unittest.TestCase):
                          "Attempted shutdown at failed backup")
         self.assertEqual(mock_warn.call_count, 1)
 
-    @unittest.mock.patch('PyQt4.QtGui.QMessageBox.warning')
+    @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning')
     @unittest.mock.patch('os.system')
     @unittest.mock.patch('os.remove')
     @unittest.mock.patch('qubesmanager.backup_utils.write_backup_profile')
     @unittest.mock.patch('qubesadmin.Qubes.qubesd_call',
                          return_value=b'backup output')
     def test_28_progress(
-            self, _a, _b, mock_remove, mock_system, mock_warn):
+            self, _a, _b, _mock_remove, _mock_system, _mock_warn):
         self._click_next()
         self.assertTrue(self.dialog.currentPage()
                         is self.dialog.select_dir_page)
@@ -626,11 +599,11 @@ class BackupTest(unittest.TestCase):
             widget.setCurrentIndex(widget.currentIndex() + 1)
 
     def _click_next(self):
-        next_widget = self.dialog.button(QtGui.QWizard.NextButton)
+        next_widget = self.dialog.button(QtWidgets.QWizard.NextButton)
         QtTest.QTest.mouseClick(next_widget, QtCore.Qt.LeftButton)
 
     def _click_cancel(self):
-        cancel_widget = self.dialog.button(QtGui.QWizard.CancelButton)
+        cancel_widget = self.dialog.button(QtWidgets.QWizard.CancelButton)
         QtTest.QTest.mouseClick(cancel_widget, QtCore.Qt.LeftButton)
 
     def _select_vm(self, name_starts_with):

+ 11 - 30
qubesmanager/tests/test_backup_utils.py

@@ -23,48 +23,31 @@ import logging.handlers
 import unittest.mock
 import quamash
 import asyncio
-import gc
-from PyQt4 import QtGui
+import sys
+from PyQt5 import QtWidgets
 from qubesadmin import Qubes
 
 from qubesmanager import backup_utils
 
+qtapp = QtWidgets.QApplication(sys.argv)
+loop = quamash.QEventLoop(qtapp)
+asyncio.set_event_loop(loop)
+
 
 class BackupUtilsTest(unittest.TestCase):
     def setUp(self):
         super(BackupUtilsTest, self).setUp()
         self.qapp = Qubes()
-        self.qtapp = QtGui.QApplication(["test", "-style", "cleanlooks"])
-        self.loop = quamash.QEventLoop(self.qtapp)
+        self.loop = quamash.QEventLoop(qtapp)
 
     def tearDown(self):
-        # process any pending events before destroying the object
-        self.qtapp.processEvents()
-
-        # queue destroying the QApplication object, do that for any other QT
-        # related objects here too
-        self.qtapp.deleteLater()
-
-        # process any pending events (other than just queued destroy),
-        # just in case
-        self.qtapp.processEvents()
-
-        # execute main loop, which will process all events, _
-        # including just queued destroy_
-        self.loop.run_until_complete(asyncio.sleep(0))
-
-        # at this point it QT objects are destroyed, cleanup all remaining
-        # references;
-        # del other QT object here too
-        self.loop.close()
-        del self.qtapp
-        del self.loop
-        gc.collect()
+        qtapp.processEvents()
+        yield from loop.sleep(1)
         super(BackupUtilsTest, self).tearDown()
 
     def test_01_fill_apvms(self):
-        dialog = QtGui.QDialog()
-        combobox = QtGui.QComboBox()
+        dialog = QtWidgets.QDialog()
+        combobox = QtWidgets.QComboBox()
         dialog.appvm_combobox = combobox
         dialog.qubes_app = self.qapp
 
@@ -87,8 +70,6 @@ class BackupUtilsTest(unittest.TestCase):
                              "VM list not filled correctly")
 
 
-
-
 if __name__ == "__main__":
     ha_syslog = logging.handlers.SysLogHandler('/dev/log')
     ha_syslog.setFormatter(

+ 7 - 33
qubesmanager/tests/test_create_new_vm.py

@@ -20,25 +20,22 @@
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 #
 import logging.handlers
-import quamash
-import asyncio
 import unittest
 import unittest.mock
 import qubesadmin
-import gc
 
-from PyQt4 import QtGui, QtTest, QtCore
+from PyQt5 import QtTest, QtCore
 from qubesadmin import Qubes
+from qubesmanager.tests import init_qtapp
 from qubesmanager import create_new_vm
 
 
 class NewVmTest(unittest.TestCase):
     def setUp(self):
         super(NewVmTest, self).setUp()
+        self.qtapp, self.loop = init_qtapp()
 
         self.qapp = Qubes()
-        self.qtapp = QtGui.QApplication(["test", "-style", "cleanlooks"])
-        self.loop = quamash.QEventLoop(self.qtapp)
 
         # mock up the Create VM Thread to avoid changing system state
         self.patcher_thread = unittest.mock.patch(
@@ -48,39 +45,16 @@ class NewVmTest(unittest.TestCase):
 
         # mock the progress dialog to speed testing up
         self.patcher_progress = unittest.mock.patch(
-            'PyQt4.QtGui.QProgressDialog')
+            'PyQt5.QtWidgets.QProgressDialog')
         self.mock_progress = self.patcher_progress.start()
         self.addCleanup(self.patcher_progress.stop)
 
-        self.dialog = create_new_vm.NewVmDlg(
-            self.qtapp, self.qapp)
+        self.dialog = create_new_vm.NewVmDlg(self.qtapp, self.qapp)
 
     def tearDown(self):
-        # process any pending events before destroying the object
+        self.dialog.close()
         self.qtapp.processEvents()
-
-        # queue destroying the QApplication object, do that for any other QT
-        # related objects here too
-        self.qtapp.deleteLater()
-        self.dialog.deleteLater()
-
-        # process any pending events (other than just queued destroy),
-        # just in case
-        self.qtapp.processEvents()
-
-        # execute main loop, which will process all events, _
-        # including just queued destroy_
-        self.loop.run_until_complete(asyncio.sleep(0))
-
-        # at this point it QT objects are destroyed, cleanup all remaining
-        # references;
-        # del other QT object here too
-        self.loop.close()
-        del self.dialog
-        del self.qtapp
-        del self.loop
-        gc.collect()
-
+        yield from self.loop.sleep(1)
         super(NewVmTest, self).tearDown()
 
     def test_00_window_loads(self):

+ 9 - 33
qubesmanager/tests/test_global_settings.py

@@ -20,24 +20,21 @@
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 #
 import logging.handlers
-import quamash
-import asyncio
 import unittest
 import unittest.mock
-import gc
 
-from PyQt4 import QtGui, QtTest, QtCore
+from PyQt5 import QtTest, QtCore, QtWidgets
 from qubesadmin import Qubes
+from qubesmanager.tests import init_qtapp
 import qubesmanager.global_settings as global_settings
 
 
 class GlobalSettingsTest(unittest.TestCase):
     def setUp(self):
         super(GlobalSettingsTest, self).setUp()
+        self.qtapp, self.loop = init_qtapp()
 
         self.qapp = Qubes()
-        self.qtapp = QtGui.QApplication(["test", "-style", "cleanlooks"])
-        self.loop = quamash.QEventLoop(self.qtapp)
         self.dialog = global_settings.GlobalSettingsWindow(self.qtapp,
                                                            self.qapp)
 
@@ -47,30 +44,9 @@ class GlobalSettingsTest(unittest.TestCase):
         self.addCleanup(self.setattr_patcher.stop)
 
     def tearDown(self):
-        # process any pending events before destroying the object
+        self.dialog.close()
         self.qtapp.processEvents()
-
-        # queue destroying the QApplication object, do that for any other QT
-        # related objects here too
-        self.qtapp.deleteLater()
-        self.dialog.deleteLater()
-
-        # process any pending events (other than just queued destroy),
-        # just in case
-        self.qtapp.processEvents()
-
-        # execute main loop, which will process all events, _
-        # including just queued destroy_
-        self.loop.run_until_complete(asyncio.sleep(0))
-
-        # at this point it QT objects are destroyed, cleanup all remaining
-        # references;
-        # del other QT object here too
-        self.loop.close()
-        del self.dialog
-        del self.qtapp
-        del self.loop
-        gc.collect()
+        yield from self.loop.sleep(1)
         super(GlobalSettingsTest, self).tearDown()
 
     def test_00_settings_started(self):
@@ -290,8 +266,8 @@ class GlobalSettingsTest(unittest.TestCase):
         self.setattr_mock.assert_called_once_with('check_updates_vm',
                                                   not current_state)
 
-    @unittest.mock.patch("PyQt4.QtGui.QMessageBox.question",
-                         return_value=QtGui.QMessageBox.Yes)
+    @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question",
+                         return_value=QtWidgets.QMessageBox.Yes)
     @unittest.mock.patch('qubesadmin.features.Features.__setitem__')
     def test_72_set_all_vms_true(self, mock_features, msgbox):
 
@@ -308,8 +284,8 @@ class GlobalSettingsTest(unittest.TestCase):
         self.assertListEqual(call_list_expected,
                              mock_features.call_args_list)
 
-    @unittest.mock.patch("PyQt4.QtGui.QMessageBox.question",
-                         return_value=QtGui.QMessageBox.Yes)
+    @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question",
+                         return_value=QtWidgets.QMessageBox.Yes)
     @unittest.mock.patch('qubesadmin.features.Features.__setitem__')
     def test_73_set_all_vms_false(self, mock_features, msgbox):
 

+ 43 - 67
qubesmanager/tests/test_qube_manager.py

@@ -22,64 +22,39 @@
 import asyncio
 import contextlib
 import logging.handlers
-import sys
 import unittest
 import unittest.mock
 
-import gc
 import subprocess
 import datetime
 import time
 
-import quamash
-from PyQt4 import QtGui, QtTest, QtCore
+from PyQt5 import QtTest, QtCore, QtWidgets
 from qubesadmin import Qubes, events, exc
 import qubesmanager.qube_manager as qube_manager
-
+from qubesmanager.tests import init_qtapp
 
 class QubeManagerTest(unittest.TestCase):
     def setUp(self):
         super(QubeManagerTest, self).setUp()
+        self.qtapp, self.loop = init_qtapp()
 
-        self.mock_qprogress = unittest.mock.patch('PyQt4.QtGui.QProgressDialog')
+        self.mock_qprogress = unittest.mock.patch(
+            'PyQt5.QtWidgets.QProgressDialog')
         self.mock_qprogress.start()
 
         self.addCleanup(self.mock_qprogress.stop)
 
         self.qapp = Qubes()
-        self.qtapp = QtGui.QApplication(["test", "-style", "cleanlooks"])
         self.dispatcher = events.EventsDispatcher(self.qapp)
 
-        self.loop = quamash.QEventLoop(self.qtapp)
-
         self.dialog = qube_manager.VmManagerWindow(
             self.qtapp, self.qapp, self.dispatcher)
 
     def tearDown(self):
-        # process any pending events before destroying the object
-        self.qtapp.processEvents()
-
-        # queue destroying the QApplication object, do that for any other QT
-        # related objects here too
-        self.qtapp.deleteLater()
-        self.dialog.deleteLater()
-
-        # process any pending events (other than just queued destroy),
-        # just in case
+        self.dialog.close()
         self.qtapp.processEvents()
-
-        # execute main loop, which will process all events, _
-        # including just queued destroy_
-        self.loop.run_until_complete(asyncio.sleep(0))
-
-        # at this point it QT objects are destroyed, cleanup all remaining
-        # references;
-        # del other QT object here too
-        self.loop.close()
-        del self.dialog
-        del self.qtapp
-        del self.loop
-        gc.collect()
+        yield from self.loop.sleep(1)
         super(QubeManagerTest, self).tearDown()
 
     def test_000_window_loads(self):
@@ -262,8 +237,9 @@ class QubeManagerTest(unittest.TestCase):
         mock_settings.side_effect = (
             lambda x, *args, **kwargs: settings_result_dict.get(x))
 
-        with unittest.mock.patch('PyQt4.QtCore.QSettings.value', mock_settings),\
-                unittest.mock.patch('PyQt4.QtGui.QMessageBox.warning')\
+        with unittest.mock.patch('PyQt5.QtCore.QSettings.value',
+                                 mock_settings),\
+                unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning')\
                 as mock_warning:
             self.dialog = qube_manager.VmManagerWindow(
                 self.qtapp, self.qapp, self.dispatcher)
@@ -357,8 +333,8 @@ class QubeManagerTest(unittest.TestCase):
 
         self.assertFalse(self.dialog.action_set_keyboard_layout.isEnabled())
 
-    @unittest.mock.patch("PyQt4.QtGui.QMessageBox.question",
-                         return_value=QtGui.QMessageBox.Yes)
+    @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question",
+                         return_value=QtWidgets.QMessageBox.Yes)
     def test_207_update_vm_not_running(self, _):
         selected_vm = self._select_templatevm(running=False)
         self.assertIsNotNone(selected_vm, "No valid template VM found")
@@ -387,7 +363,7 @@ class QubeManagerTest(unittest.TestCase):
             mock_update.assert_called_once_with(selected_vm)
             mock_update().start.assert_called_once_with()
 
-    @unittest.mock.patch("PyQt4.QtGui.QInputDialog.getText",
+    @unittest.mock.patch("PyQt5.QtWidgets.QInputDialog.getText",
                          return_value=("command to run", True))
     def test_209_run_command_in_vm(self, _):
         selected_vm = self._select_non_admin_vm()
@@ -408,7 +384,7 @@ class QubeManagerTest(unittest.TestCase):
         self.assertFalse(self.dialog.action_run_command_in_vm.isEnabled(),
                          "Should not be able to run commands for dom0")
 
-    @unittest.mock.patch("PyQt4.QtGui.QMessageBox.warning")
+    @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.warning")
     def test_211_pausevm(self, mock_warn):
         selected_vm = self._select_non_admin_vm(running=True)
 
@@ -446,9 +422,9 @@ class QubeManagerTest(unittest.TestCase):
         self._select_non_admin_vm(running=True)
         self.assertFalse(self.dialog.action_resumevm.isEnabled())
 
-    @unittest.mock.patch("PyQt4.QtGui.QMessageBox.question",
-                         return_value=QtGui.QMessageBox.Yes)
-    @unittest.mock.patch('PyQt4.QtCore.QTimer.singleShot')
+    @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question",
+                         return_value=QtWidgets.QMessageBox.Yes)
+    @unittest.mock.patch('PyQt5.QtCore.QTimer.singleShot')
     @unittest.mock.patch('qubesmanager.qube_manager.VmShutdownMonitor')
     def test_214_shutdownvm(self, mock_monitor, mock_timer, _):
         selected_vm = self._select_non_admin_vm(running=True)
@@ -484,7 +460,7 @@ class QubeManagerTest(unittest.TestCase):
 
         self.assertFalse(self.dialog.action_removevm.isEnabled())
 
-    @unittest.mock.patch("PyQt4.QtGui.QMessageBox")
+    @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox")
     @unittest.mock.patch('qubesadmin.utils.vm_dependencies')
     def test_218_remove_vm_dependencies(self, mock_dependencies, mock_msgbox):
         action = self.dialog.action_removevm
@@ -496,8 +472,8 @@ class QubeManagerTest(unittest.TestCase):
         action.trigger()
         mock_msgbox().show.assert_called_with()
 
-    @unittest.mock.patch('PyQt4.QtGui.QMessageBox.warning')
-    @unittest.mock.patch("PyQt4.QtGui.QInputDialog.getText")
+    @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning')
+    @unittest.mock.patch("PyQt5.QtWidgets.QInputDialog.getText")
     @unittest.mock.patch('qubesadmin.utils.vm_dependencies')
     def test_219_remove_vm_no_depencies(
             self, mock_dependencies, mock_input, mock_warning):
@@ -531,10 +507,10 @@ class QubeManagerTest(unittest.TestCase):
         self._select_non_admin_vm(running=False)
         self.assertFalse(self.dialog.action_restartvm.isEnabled())
 
-    @unittest.mock.patch('PyQt4.QtCore.QTimer.singleShot')
+    @unittest.mock.patch('PyQt5.QtCore.QTimer.singleShot')
     @unittest.mock.patch('qubesmanager.qube_manager.VmShutdownMonitor')
-    @unittest.mock.patch("PyQt4.QtGui.QMessageBox.question",
-                         return_value=QtGui.QMessageBox.Yes)
+    @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question",
+                         return_value=QtWidgets.QMessageBox.Yes)
     def test_221_restartvm_running_vm(self, _msgbox, mock_monitor, _qtimer):
         selected_vm = self._select_non_admin_vm(running=True)
 
@@ -550,8 +526,8 @@ class QubeManagerTest(unittest.TestCase):
                 unittest.mock.ANY, True, unittest.mock.ANY)
 
     @unittest.mock.patch('qubesmanager.qube_manager.StartVMThread')
-    @unittest.mock.patch("PyQt4.QtGui.QMessageBox.question",
-                         return_value=QtGui.QMessageBox.Yes)
+    @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question",
+                         return_value=QtWidgets.QMessageBox.Yes)
     def test_222_restartvm_shutdown_meantime(self, _, mock_thread):
         selected_vm = self._select_non_admin_vm(running=True)
 
@@ -577,8 +553,8 @@ class QubeManagerTest(unittest.TestCase):
             self.dialog.clear_threads)
         mock_thread().start.assert_called_once_with()
 
-    @unittest.mock.patch("PyQt4.QtGui.QMessageBox.question",
-                         return_value=QtGui.QMessageBox.Yes)
+    @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question",
+                         return_value=QtWidgets.QMessageBox.Yes)
     @unittest.mock.patch('qubesmanager.qube_manager.UpdateVMThread')
     def test_224_updatevm_halted(self, mock_thread, _):
         selected_vm = self._select_non_admin_vm(running=False)
@@ -590,8 +566,8 @@ class QubeManagerTest(unittest.TestCase):
             self.dialog.clear_threads)
         mock_thread().start.assert_called_once_with()
 
-    @unittest.mock.patch("PyQt4.QtGui.QMessageBox.question",
-                         return_value=QtGui.QMessageBox.Yes)
+    @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question",
+                         return_value=QtWidgets.QMessageBox.Yes)
     def test_224_killvm(self, _):
         selected_vm = self._select_non_admin_vm(running=True)
         action = self.dialog.action_killvm
@@ -600,8 +576,8 @@ class QubeManagerTest(unittest.TestCase):
             action.trigger()
             mock_kill.assert_called_once_with()
 
-    @unittest.mock.patch("PyQt4.QtGui.QMessageBox.question",
-                         return_value=QtGui.QMessageBox.Cancel)
+    @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question",
+                         return_value=QtWidgets.QMessageBox.Cancel)
     def test_225_killvm_cancel(self, _):
         selected_vm = self._select_non_admin_vm(running=True)
         action = self.dialog.action_killvm
@@ -659,7 +635,7 @@ class QubeManagerTest(unittest.TestCase):
         mock_subprocess.assert_called_once_with('qubes-template-manager')
 
     @unittest.mock.patch('qubesmanager.common_threads.CloneVMThread')
-    @unittest.mock.patch('PyQt4.QtGui.QInputDialog.getText')
+    @unittest.mock.patch('PyQt5.QtWidgets.QInputDialog.getText')
     def test_232_clonevm(self, mock_input, mock_thread):
         action = self.dialog.action_clonevm
 
@@ -714,7 +690,7 @@ class QubeManagerTest(unittest.TestCase):
                          "Incorrect number of vms shown for cleared search box")
 
     def test_235_hide_show_toolbars(self):
-        with unittest.mock.patch('PyQt4.QtCore.QSettings.setValue')\
+        with unittest.mock.patch('PyQt5.QtCore.QSettings.setValue')\
                 as mock_setvalue:
             self.dialog.action_menubar.trigger()
             mock_setvalue.assert_called_with('view/menubar_visible', False)
@@ -741,8 +717,8 @@ class QubeManagerTest(unittest.TestCase):
         self.assertEqual(expected_number, actual_number,
                          "Incorrect number of vms shown for cleared search box")
 
-    @unittest.mock.patch('PyQt4.QtGui.QMessageBox.information')
-    @unittest.mock.patch('PyQt4.QtGui.QMessageBox.warning')
+    @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.information')
+    @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning')
     def test_300_clear_threads(self, mock_warning, mock_info):
         mock_thread_finished_ok = unittest.mock.Mock(
             spec=['isFinished', 'msg', 'msg_is_success'],
@@ -1422,8 +1398,8 @@ class QubeManagerThreadTest(unittest.TestCase):
 
 
 class VMShutdownMonitorTest(unittest.TestCase):
-    @unittest.mock.patch('PyQt4.QtGui.QMessageBox.question')
-    @unittest.mock.patch('PyQt4.QtCore.QTimer')
+    @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.question')
+    @unittest.mock.patch('PyQt5.QtCore.QTimer')
     def test_01_vm_shutdown_correct(self, mock_timer, mock_question):
         mock_vm = unittest.mock.Mock()
         mock_vm.is_running.return_value = False
@@ -1437,9 +1413,9 @@ class VMShutdownMonitorTest(unittest.TestCase):
         self.assertEqual(mock_timer.call_count, 0)
         monitor.restart_vm_if_needed.assert_called_once_with()
 
-    @unittest.mock.patch('PyQt4.QtGui.QMessageBox.question',
+    @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.question',
                          return_value=1)
-    @unittest.mock.patch('PyQt4.QtCore.QTimer.singleShot')
+    @unittest.mock.patch('PyQt5.QtCore.QTimer.singleShot')
     def test_02_vm_not_shutdown_wait(self, mock_timer, mock_question):
         mock_vm = unittest.mock.Mock()
         mock_vm.is_running.return_value = True
@@ -1453,9 +1429,9 @@ class VMShutdownMonitorTest(unittest.TestCase):
         self.assertEqual(mock_question.call_count, 1)
         self.assertEqual(mock_timer.call_count, 1)
 
-    @unittest.mock.patch('PyQt4.QtGui.QMessageBox.question',
+    @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.question',
                          return_value=0)
-    @unittest.mock.patch('PyQt4.QtCore.QTimer.singleShot')
+    @unittest.mock.patch('PyQt5.QtCore.QTimer.singleShot')
     def test_03_vm_kill(self, mock_timer, mock_question):
         mock_vm = unittest.mock.Mock()
         mock_vm.is_running.return_value = True
@@ -1472,9 +1448,9 @@ class VMShutdownMonitorTest(unittest.TestCase):
         mock_vm.kill.assert_called_once_with()
         monitor.restart_vm_if_needed.assert_called_once_with()
 
-    @unittest.mock.patch('PyQt4.QtGui.QMessageBox.question',
+    @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.question',
                          return_value=0)
-    @unittest.mock.patch('PyQt4.QtCore.QTimer.singleShot')
+    @unittest.mock.patch('PyQt5.QtCore.QTimer.singleShot')
     def test_04_check_later(self, mock_timer, mock_question):
         mock_vm = unittest.mock.Mock()
         mock_vm.is_running.return_value = True

+ 16 - 37
qubesmanager/tests/test_vm_settings.py

@@ -24,56 +24,35 @@ import unittest
 import unittest.mock
 
 import gc
-import quamash
 import asyncio
 
-from PyQt4 import QtGui, QtTest, QtCore
+from PyQt5 import QtTest, QtCore, QtWidgets
 from qubesadmin import Qubes
 import qubesmanager.settings as vm_settings
+from qubesmanager.tests import init_qtapp
 
 
 class VMSettingsTest(unittest.TestCase):
     def setUp(self):
         super(VMSettingsTest, self).setUp()
+        self.qtapp, self.loop = init_qtapp()
 
-        self.mock_qprogress = unittest.mock.patch('PyQt4.QtGui.QProgressDialog')
+        self.mock_qprogress = unittest.mock.patch(
+            'PyQt5.QtWidgets.QProgressDialog')
         self.mock_qprogress.start()
 
         self.addCleanup(self.mock_qprogress.stop)
 
         self.qapp = Qubes()
-        self.qtapp = QtGui.QApplication(["test", "-style", "cleanlooks"])
-        self.loop = quamash.QEventLoop(self.qtapp)
+        
+        if "testvm" in self.qapp.domains:
+            del self.qapp.domains["testvm"]
 
     def tearDown(self):
         del self.qapp.domains["testvm"]
-
-        # process any pending events before destroying the object
-        self.qtapp.processEvents()
-
-        # queue destroying the QApplication object, do that for any other QT
-        # related objects here too
-        self.dialog.deleteLater()
-        self.qtapp.deleteLater()
-
-        # process any pending events (other than just queued destroy),
-        # just in case
-        self.qtapp.processEvents()
-        self.qtapp.processEvents()
         self.qtapp.processEvents()
+        yield from self.loop.sleep(1)
 
-        # execute main loop, which will process all events, _
-        # including just queued destroy_
-        self.loop.run_until_complete(asyncio.sleep(0))
-
-        # at this point it QT objects are destroyed, cleanup all remaining
-        # references;
-        # del other QT object here too
-        self.loop.close()
-        del self.dialog
-        del self.qtapp
-        del self.loop
-        gc.collect()
         super(VMSettingsTest, self).tearDown()
 
     def test_00_load_correct_tab(self):
@@ -325,8 +304,8 @@ class VMSettingsTest(unittest.TestCase):
 
         # TODO are dependencies correctly processed
 
-    @unittest.mock.patch('PyQt4.QtGui.QProgressDialog')
-    @unittest.mock.patch('PyQt4.QtGui.QInputDialog.getText')
+    @unittest.mock.patch('PyQt5.QtWidgets.QProgressDialog')
+    @unittest.mock.patch('PyQt5.QtWidgets.QInputDialog.getText')
     @unittest.mock.patch('qubesmanager.settings.RenameVMThread')
     def test_11_rename_vm(self, mock_thread, mock_input, _):
         self.vm = self.qapp.add_new_vm("AppVM", "testvm", "blue")
@@ -343,8 +322,8 @@ class VMSettingsTest(unittest.TestCase):
 
 # TODO: thread tests for rename
 
-    @unittest.mock.patch('PyQt4.QtGui.QProgressDialog')
-    @unittest.mock.patch('PyQt4.QtGui.QInputDialog.getText')
+    @unittest.mock.patch('PyQt5.QtWidgets.QProgressDialog')
+    @unittest.mock.patch('PyQt5.QtWidgets.QInputDialog.getText')
     @unittest.mock.patch('qubesmanager.common_threads.CloneVMThread')
     def test_12_clone_vm(self, mock_thread, mock_input, _):
         self.vm = self.qapp.add_new_vm("AppVM", "testvm", "blue")
@@ -359,9 +338,9 @@ class VMSettingsTest(unittest.TestCase):
         mock_thread.assert_called_with(self.vm, "testvm2")
         mock_thread().start.assert_called_with()
 
-    @unittest.mock.patch('PyQt4.QtGui.QMessageBox.warning')
-    @unittest.mock.patch('PyQt4.QtGui.QProgressDialog')
-    @unittest.mock.patch('PyQt4.QtGui.QInputDialog.getText')
+    @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning')
+    @unittest.mock.patch('PyQt5.QtWidgets.QProgressDialog')
+    @unittest.mock.patch('PyQt5.QtWidgets.QInputDialog.getText')
     @unittest.mock.patch('qubesmanager.common_threads.RemoveVMThread')
     def test_13_remove_vm(self, mock_thread, mock_input, _, mock_warning):
         self.vm = self.qapp.add_new_vm("AppVM", "testvm", "blue")