123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692 |
- #!/usr/bin/python3
- #
- # The Qubes OS Project, https://www.qubes-os.org/
- #
- # Copyright (C) 2016 Marta Marczykowska-Górecka
- # <marmarta@invisiblethingslab.com>
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License along
- # with this program; if not, write to the Free Software Foundation, Inc.,
- # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- #
- import logging.handlers
- import unittest
- import unittest.mock
- from PyQt5 import QtTest, QtCore, QtWidgets
- from qubesadmin import Qubes, events, utils, exc
- from qubesmanager import backup
- from qubesmanager.tests import init_qtapp
- class BackupTest(unittest.TestCase):
- @classmethod
- def setUpClass(cls):
- qapp = Qubes()
- cls.dom0_name = "dom0"
- cls.vms = []
- cls.running_vm = None
- for vm in qapp.domains:
- if vm.klass != "AdminVM" and vm.is_running():
- cls.running_vm = vm.name
- if vm.klass != "AdminVM" and vm.get_disk_utilization() > 0:
- cls.vms.append(vm.name)
- if cls.running_vm and len(cls.vms) >= 3:
- break
- 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')
- self.mock_open = self.patcher_open.start()
- self.mock_open.side_effect = FileNotFoundError()
- self.addCleanup(self.patcher_open.stop)
- # mock up the Backup Thread to avoid accidentally changing system state
- self.patcher_thread = unittest.mock.patch(
- 'qubesmanager.backup.BackupThread')
- self.mock_thread = self.patcher_thread.start()
- self.addCleanup(self.patcher_thread.stop)
- self.qapp = Qubes()
- self.dispatcher = events.EventsDispatcher(self.qapp)
- self.dialog = backup.BackupVMsWindow(
- self.qtapp, self.qapp, self.dispatcher)
- self.dialog.show()
- def tearDown(self):
- self.dialog.done(0)
- super(BackupTest, self).tearDown()
- def test_00_window_loads(self):
- self.assertTrue(self.dialog.select_vms_widget is not None)
- def test_01_vms_load_correctly(self):
- all_vms = len([vm for vm in self.qapp.domains
- if not vm.features.get('internal', False)])
- selected_vms = self.dialog.select_vms_widget.selected_list.count()
- available_vms = self.dialog.select_vms_widget.available_list.count()
- self.assertEqual(all_vms, available_vms + selected_vms)
- def test_02_correct_defaults(self):
- # backup is compressed
- self.assertTrue(self.dialog.compress_checkbox.isChecked(),
- "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)])
- 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")
- # save defaults
- self.assertTrue(self.dialog.save_profile_checkbox.isChecked(),
- "By default, profile should be saved")
- def test_03_select_vms_widget(self):
- number_of_all_vms = len([vm for vm in self.qapp.domains
- if not vm.features.get('internal', False)])
- # select all
- self.dialog.select_vms_widget.add_all_button.click()
- self.assertEqual(number_of_all_vms,
- self.dialog.select_vms_widget.selected_list.count(),
- "Add All VMs does not work")
- # remove all
- self.dialog.select_vms_widget.remove_all_button.click()
- self.assertEqual(number_of_all_vms,
- self.dialog.select_vms_widget.available_list.count(),
- "Remove All VMs does not work")
- self._select_vm(self.vms[0])
- self.assertEqual(self.dialog.select_vms_widget.selected_list.count(),
- 1, "Select a single VM does not work")
- def test_04_open_directory(self):
- self.dialog.next()
- self.assertTrue(self.dialog.currentPage()
- is self.dialog.select_dir_page)
- with unittest.mock.patch('qubesmanager.backup_utils.'
- 'select_path_button_clicked') as mock_func:
- self.dialog.select_path_button.click()
- mock_func.assert_called_once_with(unittest.mock.ANY)
- def test_05_running_vms_listed(self):
- self.dialog.next()
- self.assertTrue(self.dialog.currentPage()
- is self.dialog.select_dir_page)
- running_vms = [vm.name for vm in self.qapp.domains if vm.is_running()]
- listed_vms = []
- for i in range(self.dialog.appvm_combobox.count()):
- listed_vms.append(self.dialog.appvm_combobox.itemText(i))
- self.assertListEqual(sorted(running_vms), sorted(listed_vms),
- "Incorrect list of running vms")
- def test_06_passphrase_verification(self):
- self.dialog.next()
- self.assertTrue(self.dialog.currentPage()
- is self.dialog.select_dir_page)
- # required to check if next button is correctly enabled
- self.dialog.dir_line_edit.setText("/home")
- next_button = self.dialog.button(self.dialog.NextButton)
- # check if next remains inactive for various incorrect
- # passphrase/incorrect combinations
- self.dialog.passphrase_line_edit.setText("pass")
- self.dialog.passphrase_line_edit_verify.setText("fail")
- self.assertFalse(next_button.isEnabled(),
- "Mismatched passphrase/verification accepted")
- self.dialog.passphrase_line_edit.setText("pass")
- self.dialog.passphrase_line_edit_verify.setText("")
- self.assertFalse(next_button.isEnabled(), "Empty verification accepted")
- self.dialog.passphrase_line_edit.setText("")
- self.dialog.passphrase_line_edit_verify.setText("fail")
- self.assertFalse(next_button.isEnabled(), "Empty passphrase accepted")
- self.dialog.passphrase_line_edit.setText("")
- self.dialog.passphrase_line_edit_verify.setText("")
- self.assertFalse(next_button.isEnabled(),
- "Empty passphrase and verification accepted")
- # check if next is active for a correct passphrase/verify
- # combination
- self.dialog.passphrase_line_edit.setText("pass")
- self.dialog.passphrase_line_edit_verify.setText("pass")
- self.assertTrue(next_button.isEnabled(),
- "Matching passphrase/verification not accepted")
- def test_07_disk_space_correct(self):
- for i in range(self.dialog.select_vms_widget.available_list.count()):
- item = self.dialog.select_vms_widget.available_list.item(i)
- if item.vm.name == self.dom0_name or \
- item.vm.get_disk_utilization() > 0:
- self.assertGreater(
- item.size, 0,
- "{} size incorrectly reported as 0".format(item.vm.name))
- def test_08_total_size_correct(self):
- if len(self.vms) < 3:
- self.skipTest("Insufficient number of VMs with positive "
- "disk utilization")
- # select nothing
- self.dialog.select_vms_widget.remove_all_button.click()
- self.assertEqual(self.dialog.total_size_label.text(), "0",
- "Total size of 0 vms incorrectly reported as 0")
- current_size = 0
- # select a single VM
- self._select_vm(self.vms[0])
- current_size += self.qapp.domains[self.vms[0]].get_disk_utilization()
- self.assertEqual(self.dialog.total_size_label.text(),
- utils.size_to_human(current_size),
- "Size incorrectly listed for a single VM")
- # add two more
- self._select_vm(self.vms[1])
- self._select_vm(self.vms[2])
- current_size += self.qapp.domains[self.vms[1]].get_disk_utilization()
- current_size += self.qapp.domains[self.vms[2]].get_disk_utilization()
- self.assertEqual(self.dialog.total_size_label.text(),
- utils.size_to_human(current_size),
- "Size incorrectly listed for several VMs")
- # remove one
- self._deselect_vm(self.vms[0])
- current_size -= self.qapp.domains[self.vms[0]].get_disk_utilization()
- self.assertEqual(self.dialog.total_size_label.text(),
- utils.size_to_human(current_size),
- "Size incorrectly listed for several VMs")
- @unittest.mock.patch('qubesmanager.backup_utils.write_backup_profile')
- @unittest.mock.patch('qubesadmin.Qubes.qubesd_call',
- return_value=b'backup output')
- def test_10_first_backup(self, mock_qubesd, mock_write_profile):
- self.assertTrue(self.dialog.currentPage()
- is self.dialog.select_vms_page)
- self.dialog.select_vms_widget.remove_all_button.click()
- self._select_vm(self.vms[0])
- self._click_next()
- self.assertTrue(self.dialog.currentPage()
- is self.dialog.select_dir_page)
- # setup backup
- self._select_location(self.dom0_name)
- self.dialog.dir_line_edit.setText("/home")
- self.dialog.passphrase_line_edit.setText("pass")
- self.dialog.passphrase_line_edit_verify.setText("pass")
- self.dialog.save_profile_checkbox.setChecked(True)
- self.dialog.turn_off_checkbox.setChecked(False)
- self.dialog.compress_checkbox.setChecked(False)
- expected_settings = {'destination_vm': self.dom0_name,
- 'destination_path': "/home",
- 'include': [self.vms[0]],
- 'passphrase_text': "pass",
- 'compression': False}
- with unittest.mock.patch.object(self.dialog.textEdit, 'setText')\
- as mock_set_text:
- self._click_next()
- # make sure the confirmation is not empty
- self.assertTrue(self.dialog.currentPage()
- is self.dialog.confirm_page)
- mock_write_profile.assert_called_with(expected_settings, True)
- mock_qubesd.assert_called_with('dom0', 'admin.backup.Info',
- unittest.mock.ANY)
- mock_set_text.assert_called_once_with("backup output")
- # make sure the backup is executed
- self._click_next()
- self.mock_thread.assert_called_once_with(
- self.qapp.domains[self.dom0_name])
- self.mock_thread().start.assert_called_once_with()
- @unittest.mock.patch('qubesmanager.backup_utils.write_backup_profile')
- @unittest.mock.patch('qubesadmin.Qubes.qubesd_call',
- return_value=b'backup output')
- def test_11_second_backup(self, mock_qubesd, mock_write_profile):
- self.assertTrue(self.dialog.currentPage()
- is self.dialog.select_vms_page)
- self.dialog.select_vms_widget.remove_all_button.click()
- self._select_vm(self.dom0_name)
- self._select_vm(self.vms[0])
- self._select_vm(self.vms[1])
- self._click_next()
- self.assertTrue(self.dialog.currentPage()
- is self.dialog.select_dir_page)
- # setup backup
- self._select_location(self.running_vm)
- self.dialog.dir_line_edit.setText("/home")
- self.dialog.passphrase_line_edit.setText("longerPassPhrase")
- self.dialog.passphrase_line_edit_verify.setText("longerPassPhrase")
- self.dialog.save_profile_checkbox.setChecked(False)
- self.dialog.turn_off_checkbox.setChecked(False)
- self.dialog.compress_checkbox.setChecked(True)
- expected_settings = {'destination_vm': self.running_vm,
- 'destination_path': "/home",
- 'include': sorted([self.dom0_name, self.vms[0],
- self.vms[1]]),
- 'passphrase_text': "longerPassPhrase",
- 'compression': True}
- with unittest.mock.patch.object(self.dialog.textEdit, 'setText')\
- as mock_set_text:
- self._click_next()
- # make sure the confirmation is not empty
- self.assertTrue(self.dialog.currentPage()
- is self.dialog.confirm_page)
- mock_write_profile.assert_called_with(expected_settings, True)
- mock_qubesd.assert_called_with('dom0', 'admin.backup.Info',
- unittest.mock.ANY)
- mock_set_text.assert_called_once_with("backup output")
- # make sure the backup is executed
- self._click_next()
- self.mock_thread.assert_called_once_with(
- self.qapp.domains[self.running_vm])
- self.mock_thread().start.assert_called_once_with()
- @unittest.mock.patch('qubesmanager.backup_utils.load_backup_profile')
- def test_20_loading_settings(self, mock_load):
- mock_load.return_value = {
- 'destination_vm': self.running_vm,
- 'destination_path': "/home",
- 'include': [self.dom0_name, self.vms[0], self.vms[1]],
- 'passphrase_text': "longerPassPhrase",
- 'compression': True
- }
- self.dialog.hide()
- self.dialog.deleteLater()
- self.qtapp.processEvents()
- self.dialog = backup.BackupVMsWindow(
- self.qtapp, self.qapp, self.dispatcher)
- self.dialog.show()
- # check if settings were loaded
- self.assertEqual(self.dialog.appvm_combobox.currentText(),
- self.running_vm,
- "Destination VM not loaded")
- self.assertEqual(self.dialog.dir_line_edit.text(), "/home",
- "Destination path not loaded")
- self.assertEqual(self.dialog.passphrase_line_edit.text(),
- "longerPassPhrase", "Passphrase not loaded")
- self.assertEqual(self.dialog.passphrase_line_edit_verify.text(),
- "longerPassPhrase", "Passphrase verify not loaded")
- self.assertTrue(self.dialog.compress_checkbox.isChecked())
- # check that 'include' vms were not pre-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)])
- selected_no = self.dialog.select_vms_widget.selected_list.count()
- self.assertEqual(include_in_backups_no, selected_no,
- "Incorrect VM list selected")
- # check no errors were detected
- self.assertFalse(self.dialog.unrecognized_config_label.isVisible())
- @unittest.mock.patch('qubesmanager.backup_utils.load_backup_profile')
- def test_21_loading_settings_error(self, mock_load):
- mock_load.return_value = {
- 'destination_vm': "incorrect_vm",
- }
- self.dialog.hide()
- self.dialog.deleteLater()
- self.qtapp.processEvents()
- self.dialog = backup.BackupVMsWindow(
- self.qtapp, self.qapp, self.dispatcher)
- self.dialog.show()
- # check errors were detected
- self.assertTrue(self.dialog.unrecognized_config_label.isVisible())
- @unittest.mock.patch('qubesmanager.backup_utils.load_backup_profile')
- @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')
- self.dialog.hide()
- self.dialog.deleteLater()
- self.qtapp.processEvents()
- self.dialog = backup.BackupVMsWindow(
- self.qtapp, self.qapp, self.dispatcher)
- self.dialog.show()
- # check error was reported
- self.assertEqual(mock_info.call_count, 1, "Warning not shown")
- @unittest.mock.patch('qubesmanager.backup_utils.write_backup_profile')
- @unittest.mock.patch('qubesadmin.Qubes.qubesd_call',
- return_value=b'backup output')
- def test_23_cancel_confirm(self, *_args):
- self._click_next()
- self.assertTrue(self.dialog.currentPage()
- is self.dialog.select_dir_page)
- self._select_location(self.dom0_name)
- self.dialog.dir_line_edit.setText("/home")
- self.dialog.passphrase_line_edit.setText("pass")
- self.dialog.passphrase_line_edit_verify.setText("pass")
- self._click_next()
- # attempt to cancel
- with unittest.mock.patch('os.remove') as mock_remove:
- self._click_cancel()
- mock_remove.assert_called_once_with(
- '/etc/qubes/backup/qubes-manager-backup-tmp.conf')
- @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')
- def test_24_cancel_in_progress(self, mock_call, *_args):
- self._click_next()
- self.assertTrue(self.dialog.currentPage()
- is self.dialog.select_dir_page)
- self._select_location(self.dom0_name)
- self.dialog.dir_line_edit.setText("/home")
- self.dialog.passphrase_line_edit.setText("pass")
- self.dialog.passphrase_line_edit_verify.setText("pass")
- self._click_next()
- self._click_next()
- # attempt to cancel
- with unittest.mock.patch('os.remove') as mock_remove:
- self._click_cancel()
- mock_call.assert_called_with('dom0', 'admin.backup.Cancel',
- 'qubes-manager-backup-tmp')
- mock_remove.assert_called_once_with(
- '/etc/qubes/backup/qubes-manager-backup-tmp.conf')
- @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_25_successful_backup(self, _a, _b, mock_remove,
- mock_system, mock_warning):
- self._click_next()
- self.assertTrue(self.dialog.currentPage()
- is self.dialog.select_dir_page)
- self._select_location(self.dom0_name)
- self.dialog.dir_line_edit.setText("/home")
- self.dialog.passphrase_line_edit.setText("pass")
- self.dialog.passphrase_line_edit_verify.setText("pass")
- self.dialog.turn_off_checkbox.setChecked(False)
- self._click_next()
- self._click_next()
- # assume backup went correctly
- self.mock_thread().msg = None
- self.mock_thread().finished.connect.assert_called_once_with(
- self.dialog.backup_finished)
- self.dialog.backup_finished()
- self.assertFalse(self.dialog.button(
- self.dialog.CancelButton).isEnabled())
- self.assertTrue(self.dialog.button(
- self.dialog.FinishButton).isEnabled())
- mock_remove.assert_called_once_with(
- '/etc/qubes/backup/qubes-manager-backup-tmp.conf')
- self.assertEqual(mock_system.call_count, 0,
- "System turned off unnecessarily")
- self.assertEqual(mock_warning.call_count, 0,
- "Backup succeeded but received 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_26_success_backup_poweroff(
- self, _a, _b, mock_remove, mock_system, mock_warning):
- self._click_next()
- self.assertTrue(self.dialog.currentPage()
- is self.dialog.select_dir_page)
- self._select_location(self.dom0_name)
- self.dialog.dir_line_edit.setText("/home")
- self.dialog.passphrase_line_edit.setText("pass")
- self.dialog.passphrase_line_edit_verify.setText("pass")
- self.dialog.turn_off_checkbox.setChecked(True)
- self._click_next()
- self._click_next()
- # assume backup went correctly
- self.mock_thread().msg = None
- self.mock_thread().finished.connect.assert_called_once_with(
- self.dialog.backup_finished)
- self.dialog.backup_finished()
- self.assertFalse(self.dialog.button(
- self.dialog.CancelButton).isEnabled())
- self.assertTrue(self.dialog.button(
- self.dialog.FinishButton).isEnabled())
- mock_remove.assert_called_once_with(
- '/etc/qubes/backup/qubes-manager-backup-tmp.conf')
- mock_system.assert_called_once_with('systemctl poweroff')
- self.assertEqual(mock_warning.call_count, 0,
- "Backup succeeded but received 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_27_failed_backup(
- self, _a, _b, mock_remove, mock_system, mock_warn):
- self._click_next()
- self.assertTrue(self.dialog.currentPage()
- is self.dialog.select_dir_page)
- self._select_location(self.dom0_name)
- self.dialog.dir_line_edit.setText("/home")
- self.dialog.passphrase_line_edit.setText("pass")
- self.dialog.passphrase_line_edit_verify.setText("pass")
- self.dialog.turn_off_checkbox.setChecked(True)
- self._click_next()
- self._click_next()
- # assume backup went wrong
- self.mock_thread().msg = "Error"
- self.mock_thread().finished.connect.assert_called_once_with(
- self.dialog.backup_finished)
- self.dialog.backup_finished()
- self.assertFalse(self.dialog.button(
- self.dialog.CancelButton).isEnabled())
- self.assertTrue(self.dialog.button(
- self.dialog.FinishButton).isEnabled())
- mock_remove.assert_called_once_with(
- '/etc/qubes/backup/qubes-manager-backup-tmp.conf')
- self.assertEqual(mock_system.call_count, 0,
- "Attempted shutdown at failed backup")
- self.assertEqual(mock_warn.call_count, 1)
- @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._click_next()
- self.assertTrue(self.dialog.currentPage()
- is self.dialog.select_dir_page)
- self._select_location(self.dom0_name)
- self.dialog.dir_line_edit.setText("/home")
- self.dialog.passphrase_line_edit.setText("pass")
- self.dialog.passphrase_line_edit_verify.setText("pass")
- self.dialog.turn_off_checkbox.setChecked(True)
- self._click_next()
- self._click_next()
- # see if backup is correctly in progress
- self.assertTrue(self.dialog.button(
- self.dialog.CancelButton).isEnabled())
- self.assertFalse(self.dialog.button(
- self.dialog.FinishButton).isEnabled())
- self.assertEqual(self.dialog.progress_bar.value(), 0,
- "Progress bar does not start at 0")
- # this is not a perfect method, but it is something
- self.dialog.on_backup_progress(None, None, progress='23.3123')
- self.assertEqual(self.dialog.progress_bar.value(), 23,
- "Progress bar does not update correctly")
- self.dialog.on_backup_progress(None, None, progress='87.89')
- self.assertEqual(self.dialog.progress_bar.value(), 87,
- "Progress bar does not update correctly")
- def _select_location(self, vm_name):
- widget = self.dialog.appvm_combobox
- widget.setCurrentIndex(0)
- while not widget.currentText() == vm_name:
- if widget.currentIndex() == widget.count():
- self.skipTest("target VM not found")
- widget.setCurrentIndex(widget.currentIndex() + 1)
- def _click_next(self):
- 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(QtWidgets.QWizard.CancelButton)
- QtTest.QTest.mouseClick(cancel_widget, QtCore.Qt.LeftButton)
- def _select_vm(self, name_starts_with):
- for i in range(self.dialog.select_vms_widget.available_list.count()):
- item = self.dialog.select_vms_widget.available_list.item(i)
- if item.text().startswith(name_starts_with):
- item.setSelected(True)
- self.dialog.select_vms_widget.add_selected_button.click()
- return
- def _deselect_vm(self, name_starts_with):
- for i in range(self.dialog.select_vms_widget.selected_list.count()):
- item = self.dialog.select_vms_widget.selected_list.item(i)
- if item.text().startswith(name_starts_with):
- item.setSelected(True)
- self.dialog.select_vms_widget.remove_selected_button.click()
- return
- class BackupThreadTest(unittest.TestCase):
- def test_01_backup_thread_vm_on(self):
- vm = unittest.mock.Mock(spec=['is_running', 'app'],
- **{'is_running.return_value': True})
- vm.app = unittest.mock.Mock()
- thread = backup.BackupThread(vm)
- thread.run()
- vm.app.qubesd_call.assert_called_with(
- 'dom0', 'admin.backup.Execute', 'qubes-manager-backup-tmp')
- def test_02_backup_thread_vm_off(self):
- vm = unittest.mock.Mock(spec=['is_running', 'app', 'start'],
- **{'is_running.return_value': False})
- vm.app = unittest.mock.Mock()
- thread = backup.BackupThread(vm)
- thread.run()
- vm.app.qubesd_call.assert_called_with(
- 'dom0', 'admin.backup.Execute', 'qubes-manager-backup-tmp')
- vm.start.assert_called_once_with()
- def test_03_backup_thread_error(self):
- vm = unittest.mock.Mock(spec=['is_running', 'app'],
- **{'is_running.return_value': True})
- vm.app = unittest.mock.Mock()
- vm.app.qubesd_call.side_effect = exc.QubesException('Error')
- thread = backup.BackupThread(vm)
- thread.run()
- self.assertIsNotNone(thread.msg)
- if __name__ == "__main__":
- ha_syslog = logging.handlers.SysLogHandler('/dev/log')
- ha_syslog.setFormatter(
- logging.Formatter('%(name)s[%(process)d]: %(message)s'))
- logging.root.addHandler(ha_syslog)
- unittest.main()
|