Unit test improvements and coverage increase. By @bigtedde (#1787)

This commit is contained in:
Ted Lawson 2023-10-01 01:19:39 -07:00 committed by GitHub
parent c807f93faf
commit 60f9fc27b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 338 additions and 120 deletions

View File

@ -325,7 +325,7 @@ class VortaApp(QtSingleApplication):
# No fail
logger.warning('VortaApp.check_failed_response was called with returncode 0')
elif returncode == 130:
# Keyboard interupt
# Keyboard interrupt
pass
else: # Real error
# Create QMessageBox

View File

@ -1489,11 +1489,11 @@
</message>
<message>
<location filename="../../views/import_window.py" line="69"/>
<source>Schema upgrade failure, file a bug report with the link in the Misc tab with the following error:
{0}
<source>Schema upgrade failure, file a bug report with the link in the Misc tab with the following error:
{0}
{1}</source>
<translation>Schema-Upgrade Fehler, erstelle einen Bugreport auf dem Link um &quot;Misc&quot;-Tab, mit folgendem Fehler:
{0}
<translation>Schema-Upgrade Fehler, erstelle einen Bugreport auf dem Link um &quot;Misc&quot;-Tab, mit folgendem Fehler:
{0}
{1}</translation>
</message>
<message>
@ -2267,4 +2267,4 @@
<translation>Speichere Kennwort in der Vortakonfiguration</translation>
</message>
</context>
</TS>
</TS>

View File

@ -1491,8 +1491,8 @@
</message>
<message>
<location filename="../../views/import_window.py" line="69"/>
<source>Schema upgrade failure, file a bug report with the link in the Misc tab with the following error:
{0}
<source>Schema upgrade failure, file a bug report with the link in the Misc tab with the following error:
{0}
{1}</source>
<translation>Fallo al actualizar el esquema, rellene un informe de error con el enlace de la pestaña «Varios» con el siguiente error:
{0}
@ -2269,4 +2269,4 @@
<translation>Guardar contraseñas con los ajustes de Vorta.</translation>
</message>
</context>
</TS>
</TS>

View File

@ -1491,11 +1491,11 @@
</message>
<message>
<location filename="../../views/import_window.py" line="69"/>
<source>Schema upgrade failure, file a bug report with the link in the Misc tab with the following error:
{0}
<source>Schema upgrade failure, file a bug report with the link in the Misc tab with the following error:
{0}
{1}</source>
<translation>Skeeman päivitys epäonnistui, lähetä virheraportti Sekalaiset-välilehdellä olevasta linkistä. Liitä raporttiin seuraavat tiedot:
{0}
<translation>Skeeman päivitys epäonnistui, lähetä virheraportti Sekalaiset-välilehdellä olevasta linkistä. Liitä raporttiin seuraavat tiedot:
{0}
{1}</translation>
</message>
<message>
@ -2269,4 +2269,4 @@
<translation>Tallennetaan salasana Vortan asetuksiin.</translation>
</message>
</context>
</TS>
</TS>

View File

@ -6,6 +6,7 @@ import vorta.borg
import vorta.utils
import vorta.views.archive_tab
from PyQt6 import QtCore
from PyQt6.QtWidgets import QMenu
from vorta.store.models import ArchiveModel, BackupProfileModel
@ -202,3 +203,18 @@ def test_inline_archive_rename(qapp, qtbot, mocker, borg_json_output, archive_en
# Successful rename case
qtbot.waitUntil(lambda: tab.archiveTable.model().index(0, 4).data() == new_archive_name, **pytest._wait_defaults)
assert tab.archiveTable.model().index(0, 4).data() == new_archive_name
def test_archiveitem_contextmenu(qapp, qtbot, archive_env):
main, tab = archive_env
pos = tab.archiveTable.visualRect(tab.archiveTable.model().index(0, 0)).center()
tab.archiveTable.customContextMenuRequested.emit(pos)
qtbot.waitUntil(lambda: tab.archiveTable.findChild(QMenu) is not None, timeout=2000)
context_menu = tab.archiveTable.findChild(QMenu)
assert context_menu is not None
expected_actions = ['Copy', 'Recalculate', 'Mount…', 'Extract…', 'Rename…', 'Delete', 'Diff']
for action in expected_actions:
assert any(menu_actions.text() == action for menu_actions in context_menu.actions())

View File

@ -5,6 +5,7 @@ import vorta.borg
import vorta.utils
import vorta.views.archive_tab
from PyQt6.QtCore import QDateTime, QItemSelectionModel, Qt
from PyQt6.QtWidgets import QMenu
from vorta.views.diff_result import (
ChangeType,
DiffData,
@ -15,12 +16,8 @@ from vorta.views.diff_result import (
)
@pytest.mark.parametrize(
'json_mock_file,folder_root', [('diff_archives', 'test'), ('diff_archives_dict_issue', 'Users')]
)
def test_archive_diff(qapp, qtbot, mocker, borg_json_output, json_mock_file, folder_root, archive_env):
main, tab = archive_env
def setup_diff_result_window(qtbot, mocker, tab, borg_json_output, json_mock_file="diff_archives"):
"""Sets up the diff result window."""
stdout, stderr = borg_json_output(json_mock_file)
popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0)
mocker.patch.object(vorta.borg.borg_job, 'Popen', return_value=popen_result)
@ -46,14 +43,70 @@ def test_archive_diff(qapp, qtbot, mocker, borg_json_output, json_mock_file, fol
tab.diff_action()
qtbot.waitUntil(lambda: hasattr(tab, '_resultwindow'), **pytest._wait_defaults)
assert hasattr(tab, '_resultwindow')
@pytest.mark.parametrize(
'json_mock_file, folder_root', [('diff_archives', 'test'), ('diff_archives_dict_issue', 'Users')]
)
def test_archive_diff(qapp, qtbot, mocker, borg_json_output, json_mock_file, folder_root, archive_env):
"""Tests basic functionality of archive diff."""
main, tab = archive_env
setup_diff_result_window(qtbot, mocker, tab, borg_json_output, json_mock_file)
model = tab._resultwindow.treeView.model().sourceModel()
assert model.root.children[0].subpath == folder_root
assert tab._resultwindow.archiveNameLabel_1.text() == 'test-archive'
tab._resultwindow.accept()
def test_diff_item_copy(qapp, qtbot, mocker, borg_json_output, archive_env):
"""Tests copy action by row selection and when passed an index."""
main, tab = archive_env
setup_diff_result_window(qtbot, mocker, tab, borg_json_output)
# mock the clipboard to ensure no changes are made to it during testing
mocker.patch.object(qapp.clipboard(), "setMimeData")
clipboard_spy = mocker.spy(qapp.clipboard(), "setMimeData")
# test 'diff_item_copy()' by passing it an item to copy
index = tab._resultwindow.treeView.model().index(0, 0)
assert index is not None
tab._resultwindow.diff_item_copy(index)
clipboard_data = clipboard_spy.call_args[0][0]
assert clipboard_data.hasText()
assert clipboard_data.text() == "/test"
clipboard_spy.reset_mock()
# test 'diff_item_copy()' by selecting a row to copy
flags = QItemSelectionModel.SelectionFlag.Rows
flags |= QItemSelectionModel.SelectionFlag.Select
tab._resultwindow.treeView.selectionModel().select(tab._resultwindow.treeView.model().index(0, 0), flags)
tab._resultwindow.diff_item_copy()
clipboard_data = clipboard_spy.call_args[0][0]
assert clipboard_data.hasText()
assert clipboard_data.text() == "/test"
def test_treeview_context_menu(qapp, qtbot, mocker, borg_json_output, archive_env):
"""Tests the diff result window context menu for expected actions."""
main, tab = archive_env
setup_diff_result_window(qtbot, mocker, tab, borg_json_output)
# Load the context menu at the first result in window
pos = tab._resultwindow.treeView.visualRect(tab._resultwindow.treeView.model().index(0, 0)).center()
tab._resultwindow.treeview_context_menu(pos)
qtbot.waitUntil(lambda: tab._resultwindow.findChild(QMenu) is not None, **pytest._wait_defaults)
context_menu = tab._resultwindow.findChild(QMenu)
assert context_menu is not None
# assert the actions are available in the context menu
expected_actions = ['Copy', 'Expand recursively']
for action in expected_actions:
assert any(menu_actions.text() == action for menu_actions in context_menu.actions())
@pytest.mark.parametrize(
'line, expected',
[
@ -404,59 +457,3 @@ def test_archive_diff_json_parser(line, expected):
assert item.path == PurePath(expected[0]).parts
assert item.data == DiffData(*expected[1:])
def test_diff_item_copy(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)
mocker.patch.object(vorta.borg.borg_job, 'Popen', return_value=popen_result)
compat = vorta.utils.borg_compat
def check(feature_name):
if feature_name == 'DIFF_JSON_LINES':
return False
return vorta.utils.BorgCompatibility.check(compat, feature_name)
mocker.patch.object(vorta.utils.borg_compat, 'check', check)
selection_model: QItemSelectionModel = tab.archiveTable.selectionModel()
model = tab.archiveTable.model()
flags = QItemSelectionModel.SelectionFlag.Rows
flags |= QItemSelectionModel.SelectionFlag.Select
selection_model.select(model.index(0, 0), flags)
selection_model.select(model.index(1, 0), flags)
tab.diff_action()
qtbot.waitUntil(lambda: hasattr(tab, '_resultwindow'), **pytest._wait_defaults)
# mock the clipboard to ensure no changes are made to it during testing
mocker.patch.object(qapp.clipboard(), "setMimeData")
clipboard_spy = mocker.spy(qapp.clipboard(), "setMimeData")
# test 'diff_item_copy()' by passing it an item to copy
index = tab._resultwindow.treeView.model().index(0, 0)
assert index is not None
tab._resultwindow.diff_item_copy(index)
clipboard_data = clipboard_spy.call_args[0][0]
assert clipboard_data.hasText()
assert clipboard_data.text() == "/test"
clipboard_spy.reset_mock()
# test 'diff_item_copy()' by selecting a row to copy
tab._resultwindow.treeView.selectionModel().select(tab._resultwindow.treeView.model().index(0, 0), flags)
tab._resultwindow.diff_item_copy()
clipboard_data = clipboard_spy.call_args[0][0]
assert clipboard_data.hasText()
assert clipboard_data.text() == "/test"

View File

@ -6,33 +6,74 @@ from unittest.mock import Mock
import pytest
import vorta.store.models
from PyQt6 import QtCore
from PyQt6.QtWidgets import QCheckBox, QFormLayout
from PyQt6.QtGui import QCloseEvent
from PyQt6.QtWidgets import QCheckBox, QFormLayout, QMessageBox
from vorta.store.models import SettingsModel
def test_autostart(qapp, qtbot):
"""Check if file exists only on Linux, otherwise just check it doesn't crash"""
def test_toggle_all_settings(qapp, qtbot):
"""Toggle each setting twice as a basic sanity test to ensure app does crash."""
groups = (
SettingsModel.select(SettingsModel.group)
.distinct(True)
.where(SettingsModel.group != '')
.order_by(SettingsModel.group.asc())
)
settings = [
setting
for group in groups
for setting in SettingsModel.select().where(
SettingsModel.type == 'checkbox', SettingsModel.group == group.group
)
]
for setting in settings:
for _ in range(2):
_click_toggle_setting(setting.label, qapp, qtbot)
@pytest.mark.skipif(sys.platform != "linux", reason="testing autostart path for Linux only")
def test_autostart_linux(qapp, qtbot):
"""Checks that autostart path is added correctly on Linux when setting is enabled."""
setting = "Automatically start Vorta at login"
# ensure file is present when autostart is enabled
_click_toggle_setting(setting, qapp, qtbot)
autostart_path = (
Path(os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~") + '/.config') + "/autostart") / "vorta.desktop"
)
qtbot.waitUntil(lambda: autostart_path.exists(), **pytest._wait_defaults)
with open(autostart_path) as desktop_file:
desktop_file_text = desktop_file.read()
assert desktop_file_text.startswith("[Desktop Entry]")
if sys.platform == 'linux':
autostart_path = (
Path(os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~") + '/.config') + "/autostart")
/ "vorta.desktop"
)
qtbot.waitUntil(lambda: autostart_path.exists(), **pytest._wait_defaults)
with open(autostart_path) as desktop_file:
desktop_file_text = desktop_file.read()
assert desktop_file_text.startswith("[Desktop Entry]")
# ensure file is removed when autostart is disabled
_click_toggle_setting(setting, qapp, qtbot)
if sys.platform == 'linux':
assert not os.path.exists(autostart_path)
def test_enable_background_question(qapp, monkeypatch, mocker):
"""Tests that 'enable background question' correctly prompts user."""
main = qapp.main_window
close_event = Mock(value=QCloseEvent())
# disable system trey and enable setting to test
monkeypatch.setattr("vorta.views.main_window.is_system_tray_available", lambda: False)
mocker.patch.object(vorta.store.models.SettingsModel, "get", return_value=Mock(value=True))
mocker.patch.object(QMessageBox, "exec") # prevent QMessageBox from stopping test
# Create a mock for QMessageBox and its setText method
mock_msgbox = mocker.Mock(spec=QMessageBox)
mocker.patch("vorta.views.main_window.QMessageBox", return_value=mock_msgbox)
main.closeEvent(close_event)
mock_msgbox.setText.assert_called_once_with("Should Vorta continue to run in the background?")
close_event.accept.assert_called_once()
def test_enable_fixed_units(qapp, qtbot, mocker):
"""Tests the 'enable fixed units' setting to ensure the archive tab sizes are displayed correctly."""
tab = qapp.main_window.archiveTab
@ -61,14 +102,13 @@ def test_enable_fixed_units(qapp, qtbot, mocker):
assert kwargs_list['fixed_unit'] is None
# use the qt bot to click the setting and see that the refresh_archive emit works as intended.
with qtbot.waitSignal(qapp.main_window.miscTab.refresh_archive, timeout=5000):
with qtbot.waitSignal(qapp.main_window.miscTab.refresh_archive, **pytest._wait_defaults):
_click_toggle_setting(setting, qapp, qtbot)
@pytest.mark.skipif(sys.platform != 'darwin', reason="Full Disk Access check only on Darwin")
def test_check_full_disk_access(qapp, qtbot, mocker):
"""Enables/disables 'Check for Full Disk Access on startup' setting and ensures functionality"""
setting = "Check for Full Disk Access on startup"
"""Tests if the full disk access warning is properly silenced with the setting enabled"""
# Set mocks for setting enabled
mocker.patch.object(vorta.store.models.SettingsModel, "get", return_value=Mock(value=True))
@ -88,10 +128,6 @@ def test_check_full_disk_access(qapp, qtbot, mocker):
qapp.check_darwin_permissions()
mock_qmessagebox.assert_not_called()
# Checks that setting doesn't crash program when click toggled on then off"""
_click_toggle_setting(setting, qapp, qtbot)
_click_toggle_setting(setting, qapp, qtbot)
def _click_toggle_setting(setting, qapp, qtbot):
"""Toggle setting checkbox in the misc tab"""

View File

@ -1,37 +1,49 @@
from PyQt6 import QtCore
from PyQt6.QtWidgets import QDialogButtonBox
from PyQt6.QtWidgets import QDialogButtonBox, QMessageBox, QToolTip
from vorta.store.models import BackupProfileModel
def test_profile_add(qapp, qtbot):
def test_profile_add_delete(qapp, qtbot, mocker):
"""Tests adding and deleting profiles."""
main = qapp.main_window
# add profile and ensure it is created as intended
qtbot.mouseClick(main.profileAddButton, QtCore.Qt.MouseButton.LeftButton)
add_profile_window = main.window
# qtbot.addWidget(add_profile_window)
qtbot.keyClicks(add_profile_window.profileNameField, 'Test Profile')
qtbot.mouseClick(
add_profile_window.buttonBox.button(QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton
)
save_button = add_profile_window.buttonBox.button(QDialogButtonBox.StandardButton.Save)
qtbot.mouseClick(save_button, QtCore.Qt.MouseButton.LeftButton)
assert BackupProfileModel.get_or_none(name='Test Profile') is not None
assert main.profileSelector.currentText() == 'Test Profile'
# delete the new profile and ensure it is no longer available.
mocker.patch.object(QMessageBox, 'question', return_value=QMessageBox.StandardButton.Yes)
qtbot.mouseClick(main.profileDeleteButton, QtCore.Qt.MouseButton.LeftButton)
assert BackupProfileModel.get_or_none(name='Test Profile') is None
assert main.profileSelector.currentText() == 'Default'
# attempt to delete the last remaining profile
# see that it cannot be deleted, a warning is displayed, and the profile remains
warning = mocker.patch.object(QToolTip, 'showText')
qtbot.mouseClick(main.profileDeleteButton, QtCore.Qt.MouseButton.LeftButton)
assert "Cannot delete the last profile." in warning.call_args[0][1]
assert BackupProfileModel.get_or_none(name='Default') is not None
assert main.profileSelector.currentText() == 'Default'
def test_profile_edit(qapp, qtbot):
"""Tests editing/renaming a profile"""
main = qapp.main_window
# click to rename profile, clear the name field, type new profile name
qtbot.mouseClick(main.profileRenameButton, QtCore.Qt.MouseButton.LeftButton)
edit_profile_window = main.window
# qtbot.addWidget(edit_profile_window)
edit_profile_window.profileNameField.setText("")
qtbot.keyClicks(edit_profile_window.profileNameField, 'Test Profile')
qtbot.mouseClick(
edit_profile_window.buttonBox.button(QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton
)
save_button = edit_profile_window.buttonBox.button(QDialogButtonBox.StandardButton.Save)
qtbot.mouseClick(save_button, QtCore.Qt.MouseButton.LeftButton)
# assert a profile by the old name no longer exists, and the newly named profile does exist and is selected.
assert BackupProfileModel.get_or_none(name='Default') is None
assert BackupProfileModel.get_or_none(name='Test Profile') is not None
assert main.profileSelector.currentText() == 'Test Profile'

View File

@ -1,5 +1,6 @@
import os
import uuid
from typing import Any, Dict
import pytest
import vorta.borg.borg_job
@ -187,6 +188,55 @@ def test_ssh_dialog_failure(qapp, qtbot, mocker, monkeypatch, tmpdir):
assert tab.sshComboBox.count() == 1
def test_ssh_copy_to_clipboard_action(qapp, qtbot, mocker, tmpdir):
"""Testing the proper QMessageBox dialogue appears depending on the copy action circumstances."""
tab = qapp.main_window.repoTab
# set mocks to test assertions and prevent test interruptions
text = mocker.patch.object(QMessageBox, "setText")
mocker.patch.object(QMessageBox, "show")
mocker.patch.object(qapp.clipboard(), "setText")
qtbot.mouseClick(tab.bAddSSHKey, QtCore.Qt.MouseButton.LeftButton)
ssh_dialog = tab._window
ssh_dialog_closed = mocker.spy(ssh_dialog, 'reject')
ssh_dir = tmpdir
key_tmpfile = ssh_dir.join("id_rsa-test")
pub_tmpfile = ssh_dir.join("id_rsa-test.pub")
key_tmpfile_full = os.path.join(key_tmpfile.dirname, key_tmpfile.basename)
ssh_dialog.outputFileTextBox.setText(key_tmpfile_full)
ssh_dialog.generate_key()
# Ensure new key file was created
qtbot.waitUntil(lambda: ssh_dialog_closed.called, **pytest._wait_defaults)
assert len(ssh_dir.listdir()) == 2
# populate the ssh combobox with the ssh key we created in tmpdir
mock_expanduser = mocker.patch('os.path.expanduser', return_value=str(tmpdir))
tab.init_ssh()
assert tab.sshComboBox.count() == 2
# test when no ssh key is selected to copy
assert tab.sshComboBox.currentIndex() == 0
qtbot.mouseClick(tab.sshKeyToClipboardButton, QtCore.Qt.MouseButton.LeftButton)
message = "Select a public key from the dropdown first."
text.assert_called_with(message)
# Select a key and copy it
mock_expanduser.return_value = pub_tmpfile
tab.sshComboBox.setCurrentIndex(1)
assert tab.sshComboBox.currentIndex() == 1
qtbot.mouseClick(tab.sshKeyToClipboardButton, QtCore.Qt.MouseButton.LeftButton)
message = "The selected public SSH key was copied to the clipboard. Use it to set up remote repo permissions."
text.assert_called_with(message)
# handle ssh key file not found
mock_expanduser.return_value = "foobar"
assert tab.sshComboBox.currentIndex() == 1
qtbot.mouseClick(tab.sshKeyToClipboardButton, QtCore.Qt.MouseButton.LeftButton)
message = "Could not find public key."
text.assert_called_with(message)
def test_create(qapp, borg_json_output, mocker, qtbot):
main = qapp.main_window
stdout, stderr = borg_json_output('create')
@ -202,3 +252,56 @@ def test_create(qapp, borg_json_output, mocker, qtbot):
assert main.createStartBtn.isEnabled()
assert main.archiveTab.archiveTable.rowCount() == 3
assert main.scheduleTab.logTableWidget.rowCount() == 1
@pytest.mark.parametrize(
"response",
[
{
"return_code": 0, # no error
"error": "",
"icon": None,
"info": None,
},
{
"return_code": 1, # warning
"error": "Borg exited with warning status (rc 1).",
"icon": QMessageBox.Icon.Warning,
"info": "",
},
{
"return_code": 2, # critical error
"error": "Repository data check for repo test_repo_url failed. Error code 2",
"icon": QMessageBox.Icon.Critical,
"info": "Consider repairing or recreating the repository soon to avoid missing data.",
},
{
"return_code": 135, # 128 + n = kill signal n
"error": "killed by signal 7",
"icon": QMessageBox.Icon.Critical,
"info": "The process running the check job got a kill signal. Try again.",
},
{"return_code": 130, "error": "", "icon": None, "info": None}, # keyboard interrupt
],
)
def test_repo_check_failed_response(qapp, qtbot, mocker, response):
"""Test the processing of the signal that a repo consistency check has failed."""
mock_result: Dict[str, Any] = {
'params': {'repo_url': 'test_repo_url'},
'returncode': response["return_code"],
'errors': [(0, 'test_error_message')] if response["return_code"] not in [0, 130] else None,
}
mock_exec = mocker.patch.object(QMessageBox, "exec")
mock_text = mocker.patch.object(QMessageBox, "setText")
mock_info = mocker.patch.object(QMessageBox, "setInformativeText")
mock_icon = mocker.patch.object(QMessageBox, "setIcon")
qapp.check_failed_response(mock_result)
# return codes 0 and 130 do not provide a message
# for all other return codes, assert the message is formatted correctly
if mock_exec.call_count != 0:
mock_icon.assert_called_with(response["icon"])
assert response["error"] in mock_text.call_args[0][0]
assert response["info"] in mock_info.call_args[0][0]

View File

@ -2,6 +2,8 @@ import pytest
import vorta.views
from PyQt6 import QtCore
from PyQt6.QtWidgets import QMessageBox
from vorta.views.main_window import MainWindow
from vorta.views.source_tab import SourceTab
@pytest.fixture()
@ -9,11 +11,11 @@ def source_env(qapp, qtbot, monkeypatch, choose_file_dialog):
"""
Handles common setup and teardown for unit tests involving the source tab.
"""
monkeypatch.setattr(vorta.views.source_tab, "choose_file_dialog", choose_file_dialog)
main = qapp.main_window
main: MainWindow = qapp.main_window
tab: SourceTab = main.sourceTab
main.tabWidget.setCurrentIndex(1)
tab = main.sourceTab
qtbot.waitUntil(lambda: tab.sourceFilesWidget.rowCount() == 1, timeout=2000)
monkeypatch.setattr(vorta.views.source_tab, "choose_file_dialog", choose_file_dialog)
yield main, tab
@ -100,3 +102,27 @@ def test_sources_update(qapp, qtbot, mocker, source_env):
qtbot.mouseClick(tab.updateButton, QtCore.Qt.MouseButton.LeftButton)
assert tab.sourceFilesWidget.rowCount() == 2
assert update_path_info_spy.call_count == 2
def test_source_copy(qapp, qtbot, monkeypatch, mocker, source_env):
"""
Test source_copy() with and without an index passed.
If no index is passed, it should copy the first selected source
"""
main, tab = source_env
mock_clipboard = mocker.patch.object(qapp.clipboard(), "setMimeData")
tab.source_add(want_folder=True)
qtbot.waitUntil(lambda: tab.sourceFilesWidget.rowCount() == 2, **pytest._wait_defaults)
tab.sourceFilesWidget.selectRow(0)
tab.source_copy()
assert mock_clipboard.call_count == 1
source = mock_clipboard.call_args[0][0] # retrieves the QMimeData() object used in method call
assert source.text() == "/tmp"
index = tab.sourceFilesWidget.model().index(1, 0)
tab.source_copy(index)
assert mock_clipboard.call_count == 2
source = mock_clipboard.call_args[0][0] # retrieves the QMimeData() object used in method call
assert source.text() == "/tmp/another"

View File

@ -9,6 +9,7 @@ from vorta.utils import (
is_system_tray_available,
normalize_path,
pretty_bytes,
sort_sizes,
)
@ -21,6 +22,33 @@ def test_keyring():
assert keyring.get_password("vorta-repo", REPO) == UNICODE_PW
@pytest.mark.parametrize(
"input_sizes, expected_sorted",
[
# Basic ordering
(["1.0 GB", "2.0 MB", "3.0 KB"], ["3.0 KB", "2.0 MB", "1.0 GB"]),
# Multiple same units
(["3.0 GB", "2.0 GB", "1.0 GB"], ["1.0 GB", "2.0 GB", "3.0 GB"]),
# Multiple different units
(["2.0 MB", "3.0 GB", "1.0 KB", "5.0 GB"], ["1.0 KB", "2.0 MB", "3.0 GB", "5.0 GB"]),
# Larger to smaller units
(["1.0 YB", "1.0 ZB", "1.0 EB", "1.0 PB"], ["1.0 PB", "1.0 EB", "1.0 ZB", "1.0 YB"]),
# Skipping non-numeric sizes
(["2x MB", "3.0 KB", "apple GB", "1.0 GB"], ["3.0 KB", "1.0 GB"]),
# Skipping invalid suffix
(["1.0 XX", "5.0 YY", "9.0 ZZ", "1.0 MB"], ["1.0 MB"]),
# Floats with decimals
(["2.5 GB", "2.3 GB", "1.1 MB"], ["1.1 MB", "2.3 GB", "2.5 GB"]),
# Checking the same sizes across different units
(["1.0 MB", "1000.0 KB"], ["1000.0 KB", "1.0 MB"]),
# Handle empty lists
([], []),
],
)
def test_sort_sizes(input_sizes, expected_sorted):
assert sort_sizes(input_sizes) == expected_sorted
@pytest.mark.parametrize(
"precision, expected_unit",
[
@ -60,7 +88,7 @@ def test_best_unit_for_sizes_nonmetric(sizes, expected_unit):
)
def test_pretty_bytes_fixed_units(size, metric, precision, fixed_unit, expected_output):
"""
test pretty bytes when specifying a fixed unit of measurement
Test pretty bytes when specifying a fixed unit of measurement
"""
output = pretty_bytes(size, metric=metric, precision=precision, fixed_unit=fixed_unit)
assert output == expected_output
@ -131,7 +159,7 @@ def test_get_path_datasize(tmpdir):
def test_is_system_tray_available(mocker):
"""
sanity check to ensure proper behavior
Sanity check to ensure proper behavior
"""
mocker.patch('PyQt6.QtWidgets.QSystemTrayIcon.isSystemTrayAvailable', return_value=False)
assert is_system_tray_available() is False