diff --git a/src/vorta/__main__.py b/src/vorta/__main__.py index 11af719c..41814c2d 100644 --- a/src/vorta/__main__.py +++ b/src/vorta/__main__.py @@ -37,7 +37,7 @@ def main(): app = VortaApp(sys.argv, single_app=True) app.updater = get_updater() - sys.exit(app.exec_()) + sys.exit(app.exec()) if __name__ == '__main__': diff --git a/src/vorta/application.py b/src/vorta/application.py index 686d698a..1219500d 100644 --- a/src/vorta/application.py +++ b/src/vorta/application.py @@ -138,4 +138,4 @@ class VortaApp(QtSingleApplication): msg.setText(self.tr("No Borg Binary Found")) msg.setInformativeText(self.tr("Vorta was unable to locate a usable Borg Backup binary.")) msg.setStandardButtons(QMessageBox.Ok) - msg.exec_() + msg.exec() diff --git a/src/vorta/views/archive_tab.py b/src/vorta/views/archive_tab.py index 7fcef6fd..4f117d78 100644 --- a/src/vorta/views/archive_tab.py +++ b/src/vorta/views/archive_tab.py @@ -344,14 +344,7 @@ class ArchiveTab(ArchiveTabBase, ArchiveTabUI, BackupProfileMixin): def list_archive_result(self, result): self._set_status('') if result['returncode'] == 0: - archive = ArchiveModel.get(name=result['params']['archive_name']) - window = ExtractDialog(result['data'], archive) - self._toggle_all_buttons(True) - window.setParent(self, QtCore.Qt.Sheet) - self._window = window # for testing - window.show() - - if window.exec_(): + def process_result(): def receive(): extraction_folder = dialog.selectedFiles() if extraction_folder: @@ -369,6 +362,14 @@ class ArchiveTab(ArchiveTabBase, ArchiveTabUI, BackupProfileMixin): dialog = choose_file_dialog(self, self.tr("Choose Extraction Point"), want_folder=True) dialog.open(receive) + archive = ArchiveModel.get(name=result['params']['archive_name']) + window = ExtractDialog(result['data'], archive) + self._toggle_all_buttons(True) + window.setParent(self, QtCore.Qt.Sheet) + self._window = window # for testing + window.show() + window.accepted.connect(process_result) + def extract_archive_result(self, result): self._toggle_all_buttons(True) @@ -412,12 +413,12 @@ class ArchiveTab(ArchiveTabBase, ArchiveTabUI, BackupProfileMixin): self._set_status(params['message']) return - archive_name = self.selected_archive_name() - if archive_name is not None: + self.archive_name = self.selected_archive_name() + if self.archive_name is not None: if not self.confirm_dialog(trans_late('ArchiveTab', "Confirm deletion"), trans_late('ArchiveTab', "Are you sure you want to delete the archive?")): return - params['cmd'][-1] += f'::{archive_name}' + params['cmd'][-1] += f'::{self.archive_name}' thread = BorgDeleteThread(params['cmd'], params, parent=self.app) thread.updated.connect(self._set_status) @@ -430,20 +431,15 @@ class ArchiveTab(ArchiveTabBase, ArchiveTabUI, BackupProfileMixin): def delete_result(self, result): if result['returncode'] == 0: self._set_status(self.tr('Archive deleted.')) - self.list_action() + deleted_row = self.archiveTable.findItems(self.archive_name, QtCore.Qt.MatchExactly)[0].row() + self.archiveTable.removeRow(deleted_row) + ArchiveModel.get(name=self.archive_name).delete_instance() + del self.archive_name else: self._toggle_all_buttons(True) def diff_action(self): - profile = self.profile() - - window = DiffDialog(self.archiveTable) - self._toggle_all_buttons(True) - window.setParent(self, QtCore.Qt.Sheet) - self._window = window # for testing - window.show() - - if window.exec_(): + def process_result(): if window.selected_archives: self.selected_archives = window.selected_archives archive_cell_newer = self.archiveTable.item(self.selected_archives[0], 4) @@ -465,6 +461,15 @@ class ArchiveTab(ArchiveTabBase, ArchiveTabUI, BackupProfileMixin): else: self._set_status(params['message']) + profile = self.profile() + + window = DiffDialog(self.archiveTable) + self._toggle_all_buttons(True) + window.setParent(self, QtCore.Qt.Sheet) + self._window = window # for testing + window.show() + window.accepted.connect(process_result) + def list_diff_result(self, result): self._set_status('') if result['returncode'] == 0: diff --git a/src/vorta/views/extract_dialog.py b/src/vorta/views/extract_dialog.py index 0cc84c32..654674ab 100644 --- a/src/vorta/views/extract_dialog.py +++ b/src/vorta/views/extract_dialog.py @@ -1,11 +1,8 @@ -import sys import os -import datetime -from collections import namedtuple from PyQt5 import uic from PyQt5.QtCore import Qt -from PyQt5.QtWidgets import QApplication, QHeaderView +from PyQt5.QtWidgets import QHeaderView from vorta.utils import get_asset, get_dict_from_list, nested_dict from vorta.views.partials.tree_view import TreeModel @@ -88,19 +85,3 @@ class ExtractTree(TreeModel): return Qt.NoItemFlags return Qt.ItemIsEnabled | Qt.ItemIsUserCheckable - - -if __name__ == "__main__": - """ - For local testing: - - borg list --progress --info --log-json --format="{size:8d}{TAB}{mtime}{TAB}{path}{NL}" - """ - FakeArchive = namedtuple("Archive", ["name", "time"]) - app = QApplication(sys.argv) - test_list = open("/Users/manu/Downloads/nyx2-list.txt").read() - - archive = FakeArchive("test-archive", datetime.datetime.now()) - view = ExtractDialog(test_list, archive) - view.show() - sys.exit(app.exec_()) diff --git a/src/vorta/views/main_window.py b/src/vorta/views/main_window.py index 83f78254..514f62b0 100644 --- a/src/vorta/views/main_window.py +++ b/src/vorta/views/main_window.py @@ -134,6 +134,7 @@ class MainWindow(MainWindowBase, MainWindowUI): window = EditProfileWindow(rename_existing_id=self.profileSelector.currentData()) self.window = window # For tests window.setParent(self, QtCore.Qt.Sheet) + window.open() window.profile_changed.connect(self.add_profile_entry) window.rejected.connect(lambda: self.profileSelector.setCurrentIndex(self.profileSelector.currentIndex())) diff --git a/src/vorta/views/repo_add_dialog.py b/src/vorta/views/repo_add_dialog.py index 4ef6e832..4079d584 100644 --- a/src/vorta/views/repo_add_dialog.py +++ b/src/vorta/views/repo_add_dialog.py @@ -1,5 +1,5 @@ import re -from PyQt5 import uic +from PyQt5 import uic, QtCore from vorta.utils import get_private_keys, get_asset, choose_file_dialog, borg_compat from vorta.borg.init import BorgInitThread @@ -12,6 +12,8 @@ AddRepoUI, AddRepoBase = uic.loadUiType(uifile) class AddRepoWindow(AddRepoBase, AddRepoUI): + added_repo = QtCore.pyqtSignal(dict) + def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) @@ -85,7 +87,7 @@ class AddRepoWindow(AddRepoBase, AddRepoUI): def run_result(self, result): self.saveButton.setEnabled(True) if result['returncode'] == 0: - self.result = result + self.added_repo.emit(result) self.accept() else: self._set_status(self.tr('Unable to add your repository.')) diff --git a/src/vorta/views/repo_tab.py b/src/vorta/views/repo_tab.py index 6b13d98b..35aca6e9 100644 --- a/src/vorta/views/repo_tab.py +++ b/src/vorta/views/repo_tab.py @@ -66,7 +66,7 @@ class RepoTab(RepoBase, RepoUI, BackupProfileMixin): def set_repos(self): count = self.repoSelector.count() - for x in range(4, count): # Repositories are listed after 4th entry in repoSelector + for _ in range(4, count): # Repositories are listed after 4th entry in repoSelector self.repoSelector.removeItem(4) for repo in RepoModel.select(): self.repoSelector.addItem(repo.url, repo.id) @@ -113,12 +113,11 @@ class RepoTab(RepoBase, RepoUI, BackupProfileMixin): def ssh_select_action(self, index): if index == 1: ssh_add_window = SSHAddWindow() + self._window = ssh_add_window # For tests ssh_add_window.setParent(self, QtCore.Qt.Sheet) ssh_add_window.show() - if ssh_add_window.exec_(): - self.init_ssh() - else: - self.sshComboBox.setCurrentIndex(0) + ssh_add_window.accepted.connect(self.init_ssh) + ssh_add_window.rejected.connect(lambda: self.sshComboBox.setCurrentIndex(0)) else: profile = self.profile() profile.ssh_key = self.sshComboBox.itemData(index) @@ -138,8 +137,8 @@ class RepoTab(RepoBase, RepoUI, BackupProfileMixin): clipboard = QApplication.clipboard() clipboard.setText(pub_key) - msg.setText(self.tr("Public Key Copied to Clipboard")) - msg.setInformativeText(self.tr( + msg.setWindowTitle(self.tr("Public Key Copied to Clipboard")) + msg.setText(self.tr( "The selected public SSH key was copied to the clipboard. " "Use it to set up remote repo permissions.")) @@ -147,7 +146,7 @@ class RepoTab(RepoBase, RepoUI, BackupProfileMixin): msg.setText(self.tr("Couldn't find public key.")) else: msg.setText(self.tr("Select a public key from the dropdown first.")) - msg.exec_() + msg.show() def compression_select_action(self, index): profile = self.profile() @@ -163,12 +162,11 @@ class RepoTab(RepoBase, RepoUI, BackupProfileMixin): window = AddRepoWindow() else: window = ExistingRepoWindow() + self._window = window # For tests window.setParent(self, QtCore.Qt.Sheet) window.show() - if window.exec_(): - self.process_new_repo(window.result) - else: - self.repoSelector.setCurrentIndex(0) + window.added_repo.connect(self.process_new_repo) + window.rejected.connect(lambda: self.repoSelector.setCurrentIndex(0)) else: profile = self.profile() profile.repo = self.repoSelector.currentData() @@ -203,9 +201,9 @@ class RepoTab(RepoBase, RepoUI, BackupProfileMixin): repo.delete_instance(recursive=True) # This also deletes archives. self.repoSelector.setCurrentIndex(0) self.repoSelector.removeItem(selected_repo_index) - msg.setText(self.tr('Repository was Unlinked')) - msg.setInformativeText(self.tr('You can always connect it again later.')) - msg.exec_() + msg.setWindowTitle(self.tr('Repository was Unlinked')) + msg.setText(self.tr('You can always connect it again later.')) + msg.show() self.repo_changed.emit() self.init_repo_stats() @@ -219,4 +217,4 @@ class RepoTab(RepoBase, RepoUI, BackupProfileMixin): msg.setStandardButtons(QMessageBox.Ok) msg.setParent(self, QtCore.Qt.Sheet) msg.setText(self.tr("Select a repository from the dropdown first.")) - msg.exec_() + msg.show() diff --git a/tests/borg_json_output/delete_stderr.json b/tests/borg_json_output/delete_stderr.json new file mode 100644 index 00000000..8aae7e15 --- /dev/null +++ b/tests/borg_json_output/delete_stderr.json @@ -0,0 +1 @@ +{"type": "log_message", "time": 1605817314.144782, "message": "Deleting archive: test-archive1 Thu, 2020-11-19 14:20:48 [c361322b52718ac564129d24f34b203bd0e3fd573a0c66469d80e2428dffc9df] (1/1)", "levelname": "INFO", "name": "borg.archiver"} diff --git a/tests/borg_json_output/delete_stdout.json b/tests/borg_json_output/delete_stdout.json new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_archives.py b/tests/test_archives.py index a5303e6b..29d857e7 100644 --- a/tests/test_archives.py +++ b/tests/test_archives.py @@ -108,7 +108,7 @@ def test_archive_mount(qapp, qtbot, mocker, borg_json_output, monkeypatch, choos qtbot.waitUntil(lambda: tab.mountErrors.text().startswith('Un-mounted successfully.'), timeout=10000) -def test_archive_extract(qapp, qtbot, mocker, borg_json_output, monkeypatch): +def test_archive_extract(qapp, qtbot, mocker, borg_json_output): main = qapp.main_window tab = main.archiveTab main.tabWidget.setCurrentIndex(3) @@ -116,10 +116,6 @@ def test_archive_extract(qapp, qtbot, mocker, borg_json_output, monkeypatch): tab.populate_from_profile() qtbot.waitUntil(lambda: tab.archiveTable.rowCount() == 2) - monkeypatch.setattr( - vorta.views.extract_dialog.ExtractDialog, "exec_", lambda *args: True - ) - tab.archiveTable.selectRow(0) stdout, stderr = borg_json_output('list_archive') popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0) @@ -127,13 +123,14 @@ def test_archive_extract(qapp, qtbot, mocker, borg_json_output, monkeypatch): qtbot.mouseClick(tab.extractButton, QtCore.Qt.LeftButton) qtbot.waitUntil(lambda: hasattr(tab, '_window'), timeout=10000) + qtbot.waitUntil(lambda: tab._window == qapp.activeWindow(), timeout=5000) assert tab._window.treeView.model().rootItem.childItems[0].data(0) == 'Users' tab._window.treeView.model().rootItem.childItems[0].load_children() assert tab._window.archiveNameLabel.text().startswith('test-archive, 2000') -def test_archive_diff(qapp, qtbot, mocker, borg_json_output, monkeypatch): +def test_archive_delete(qapp, qtbot, mocker, borg_json_output): main = qapp.main_window tab = main.archiveTab main.tabWidget.setCurrentIndex(3) @@ -141,13 +138,25 @@ def test_archive_diff(qapp, qtbot, mocker, borg_json_output, monkeypatch): tab.populate_from_profile() qtbot.waitUntil(lambda: tab.archiveTable.rowCount() == 2) - monkeypatch.setattr( - vorta.views.diff_dialog.DiffDialog, "exec_", lambda *args: True - ) + tab.archiveTable.selectRow(0) + stdout, stderr = borg_json_output('delete') + popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0) + mocker.patch.object(vorta.borg.borg_thread, 'Popen', return_value=popen_result) + mocker.patch.object(vorta.views.archive_tab.ArchiveTab, 'confirm_dialog', lambda x, y, z: True) + qtbot.mouseClick(tab.deleteButton, QtCore.Qt.LeftButton) - monkeypatch.setattr( - tab, "selected_archives", (0, 1) - ) + qtbot.waitUntil(lambda: main.progressText.text() == 'Archive deleted.', timeout=3000) + assert ArchiveModel.select().count() == 1 + assert tab.archiveTable.rowCount() == 1 + + +def test_archive_diff(qapp, qtbot, mocker, borg_json_output): + main = qapp.main_window + tab = main.archiveTab + main.tabWidget.setCurrentIndex(3) + + tab.populate_from_profile() + qtbot.waitUntil(lambda: tab.archiveTable.rowCount() == 2) stdout, stderr = borg_json_output('diff_archives') popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0) @@ -155,11 +164,13 @@ def test_archive_diff(qapp, qtbot, mocker, borg_json_output, monkeypatch): qtbot.mouseClick(tab.diffButton, QtCore.Qt.LeftButton) qtbot.waitUntil(lambda: hasattr(tab, '_window'), timeout=5000) + qtbot.waitUntil(lambda: tab._window == qapp.activeWindow(), timeout=5000) - monkeypatch.setattr( - vorta.views.diff_result.DiffResult, "exec_", lambda *args: True - ) + tab._window.archiveTable.selectRow(0) + tab._window.archiveTable.selectRow(1) + qtbot.mouseClick(tab._window.diffButton, QtCore.Qt.LeftButton) qtbot.waitUntil(lambda: hasattr(tab, '_resultwindow'), timeout=5000) + qtbot.waitUntil(lambda: tab._resultwindow == qapp.activeWindow(), timeout=5000) assert tab._resultwindow.treeView.model().rootItem.childItems[0].data(0) == 'test' tab._resultwindow.treeView.model().rootItem.childItems[0].load_children() diff --git a/tests/test_profile.py b/tests/test_profile.py index 3b6c3126..0dc904f1 100644 --- a/tests/test_profile.py +++ b/tests/test_profile.py @@ -9,8 +9,9 @@ def test_profile_add(qapp, qtbot): qtbot.mouseClick(main.profileAddButton, QtCore.Qt.LeftButton) add_profile_window = main.window - qtbot.addWidget(add_profile_window) + qtbot.waitUntil(lambda: add_profile_window == qapp.activeWindow(), timeout=5000) + qtbot.keyClicks(add_profile_window.profileNameField, 'Test Profile') qtbot.mouseClick(add_profile_window.buttonBox.button(QDialogButtonBox.Save), QtCore.Qt.LeftButton) @@ -25,8 +26,9 @@ def test_profile_edit(qapp, qtbot): qtbot.mouseClick(main.profileRenameButton, QtCore.Qt.LeftButton) edit_profile_window = main.window - qtbot.addWidget(edit_profile_window) + qtbot.waitUntil(lambda: edit_profile_window == qapp.activeWindow(), timeout=5000) + edit_profile_window.profileNameField.setText("") qtbot.keyClicks(edit_profile_window.profileNameField, 'Test Profile') qtbot.mouseClick(edit_profile_window.buttonBox.button(QDialogButtonBox.Save), QtCore.Qt.LeftButton) diff --git a/tests/test_repo.py b/tests/test_repo.py index 82ca9193..574d4d2c 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -1,19 +1,18 @@ import os import uuid from PyQt5 import QtCore -from PyQt5.QtWidgets import QMessageBox import vorta.borg.borg_thread import vorta.models -from vorta.views.repo_add_dialog import AddRepoWindow -from vorta.views.ssh_dialog import SSHAddWindow +from vorta.keyring.abc import get_keyring from vorta.models import EventLogModel, RepoModel, ArchiveModel def test_repo_add_failures(qapp, qtbot, mocker, borg_json_output): # Add new repo window main = qapp.main_window - add_repo_window = AddRepoWindow(main) + main.repoTab.repoSelector.setCurrentIndex(1) + add_repo_window = main.repoTab._window qtbot.addWidget(add_repo_window) qtbot.keyClicks(add_repo_window.repoURL, 'aaa') @@ -25,8 +24,7 @@ def test_repo_add_failures(qapp, qtbot, mocker, borg_json_output): assert add_repo_window.errorText.text() == 'Please use a longer passphrase.' -def test_repo_unlink(qapp, qtbot, monkeypatch): - monkeypatch.setattr(QMessageBox, "exec_", lambda *args: QMessageBox.Yes) +def test_repo_unlink(qapp, qtbot): main = qapp.main_window tab = main.repoTab @@ -44,8 +42,8 @@ def test_repo_add_success(qapp, qtbot, mocker, borg_json_output): # Add new repo window main = qapp.main_window - main.repoTab.repo_added.disconnect() - add_repo_window = AddRepoWindow(main) + main.repoTab.repoSelector.setCurrentIndex(1) + add_repo_window = main.repoTab._window test_repo_url = f'vorta-test-repo.{uuid.uuid4()}.com:repo' # Random repo URL to avoid macOS keychain qtbot.keyClicks(add_repo_window.repoURL, test_repo_url) @@ -57,21 +55,22 @@ def test_repo_add_success(qapp, qtbot, mocker, borg_json_output): qtbot.mouseClick(add_repo_window.saveButton, QtCore.Qt.LeftButton) - with qtbot.waitSignal(add_repo_window.thread.result, timeout=3000) as blocker: + with qtbot.waitSignal(add_repo_window.thread.result, timeout=3000) as _: pass - main.repoTab.process_new_repo(blocker.args[0]) - - assert EventLogModel.select().count() == 1 + assert EventLogModel.select().count() == 2 assert RepoModel.get(id=2).url == test_repo_url - from vorta.keyring.abc import get_keyring keyring = get_keyring() assert keyring.get_password("vorta-repo", RepoModel.get(id=2).url) == LONG_PASSWORD + assert main.repoTab.repoSelector.currentText() == test_repo_url -def test_ssh_dialog(qtbot, tmpdir): - ssh_dialog = SSHAddWindow() +def test_ssh_dialog(qapp, qtbot, tmpdir): + main = qapp.main_window + main.repoTab.sshComboBox.setCurrentIndex(1) + ssh_dialog = main.repoTab._window + ssh_dir = tmpdir key_tmpfile = ssh_dir.join("id_rsa-test") pub_tmpfile = ssh_dir.join("id_rsa-test.pub")