Port unittests to pytest-qt (fixes #1)

This commit is contained in:
Rebecca Breu 2021-05-31 18:11:45 +02:00
parent e2dd0339d7
commit bc7ce7e09c
28 changed files with 4111 additions and 3726 deletions

View file

@ -18,11 +18,12 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt install libgl1-mesa-glx libegl1 libglib2.0-0 libxcb-image0 libxkbcommon-x11-0 libxcb-icccm4 libxcb-keysyms1-dev xserver-xephyr libfontconfig1 libxkbcommon-dev libdbus-1-3
sudo apt install libgl1-mesa-glx libegl1 libglib2.0-0 libxcb-image0 libxkbcommon-x11-0 libxcb-icccm4 libxcb-keysyms1 xserver-xephyr libfontconfig1 libxkbcommon-dev libdbus-1-3 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0
sudo apt install xvfb
python -m pip install --upgrade pip
pip install .
pip install -r requirements/dev.txt
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1920x1200x24 -ac +extension GLX
- name: Run Unittests with pytest
run: |
xvfb-run coverage run --source=beeref -m pytest -v -s

View file

@ -76,7 +76,7 @@ def main():
logger.info(f'Starting {constants.APPNAME} version {constants.VERSION}')
settings = BeeSettings()
logger.info(f'Using settings: {settings.fileName()}')
logger.info(f'Logging to: {logfile_name}')
logger.info(f'Logging to: {logfile_name()}')
CommandlineArgs(with_check=True) # Force checking
app = QtWidgets.QApplication(sys.argv)

View file

@ -137,8 +137,10 @@ class BeeSettings(QtCore.QSettings):
return values
logfile_name = os.path.join(
os.path.dirname(BeeSettings().fileName()), f'{constants.APPNAME}.log')
def logfile_name():
return os.path.join(
os.path.dirname(BeeSettings().fileName()), f'{constants.APPNAME}.log')
logging_conf = {
'version': 1,
@ -161,7 +163,7 @@ logging_conf = {
'file': {
'class': 'beeref.utils.BeeRotatingFileHandler',
'formatter': 'verbose',
'filename': logfile_name,
'filename': logfile_name(),
'maxBytes': 1024 * 1000 * 50,
'backupCount': 1,
'level': 'DEBUG',

View file

@ -43,8 +43,9 @@ def handle_sqlite_errors(func):
func(self, *args, **kwargs)
except sqlite3.Error as e:
logger.exception(f'Error while reading/writing {self.filename}')
self._close_connection()
if self.worker:
self.worker.finished.emit('', [str(e)])
self.worker.finished.emit(self.filename, [str(e)])
else:
raise BeeFileIOError(msg=str(e), filename=self.filename) from e

View file

@ -105,23 +105,23 @@ class DebugLogDialog(QtWidgets.QDialog):
def __init__(self, parent):
super().__init__(parent)
self.setWindowTitle(f'{constants.APPNAME} Debug Log')
with open(logfile_name) as f:
with open(logfile_name()) as f:
self.log_txt = f.read()
log = QtWidgets.QLabel(self.log_txt)
log.setTextInteractionFlags(
self.log = QtWidgets.QLabel(self.log_txt)
self.log.setTextInteractionFlags(
Qt.TextInteractionFlag.TextSelectableByMouse)
scroll = QtWidgets.QScrollArea(self)
scroll.setWidgetResizable(True)
scroll.setWidget(log)
scroll.setWidget(self.log)
buttons = QtWidgets.QDialogButtonBox(
QtWidgets.QDialogButtonBox.StandardButton.Close)
buttons.rejected.connect(self.reject)
copy_button = QtWidgets.QPushButton('Co&py To Clipboard')
copy_button.released.connect(self.copy_to_clipboard)
self.copy_button = QtWidgets.QPushButton('Co&py To Clipboard')
self.copy_button.released.connect(self.copy_to_clipboard)
buttons.addButton(
copy_button, QtWidgets.QDialogButtonBox.ButtonRole.ActionRole)
self.copy_button, QtWidgets.QDialogButtonBox.ButtonRole.ActionRole)
layout = QtWidgets.QVBoxLayout()
self.setLayout(layout)

View file

@ -38,6 +38,7 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin):
def __init__(self, app, parent=None):
super().__init__(parent)
self.app = app
self.parent = parent
self.settings = BeeSettings()
self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(60, 60, 60)))
@ -94,7 +95,7 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin):
name = os.path.basename(self.filename or '[Untitled]')
clean = '' if clean else '*'
title = f'{name}{clean} - {constants.APPNAME}'
self.parent().setWindowTitle(title)
self.parent.setWindowTitle(title)
def on_scene_changed(self, region):
if not self.scene.items():
@ -169,16 +170,16 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin):
def on_action_fullscreen(self, checked):
if checked:
self.parent().showFullScreen()
self.parent.showFullScreen()
else:
self.parent().showNormal()
self.parent.showNormal()
def on_action_always_on_top(self, checked):
self.parent().setWindowFlag(
self.parent.setWindowFlag(
Qt.WindowType.WindowStaysOnTopHint, on=checked)
self.parent().destroy()
self.parent().create()
self.parent().show()
self.parent.destroy()
self.parent.create()
self.parent.show()
def on_action_show_scrollbars(self, checked):
if checked:
@ -194,9 +195,9 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin):
def on_action_show_menubar(self, checked):
if checked:
self.parent().setMenuBar(self.create_menubar())
self.parent.setMenuBar(self.create_menubar())
else:
self.parent().setMenuBar(None)
self.parent.setMenuBar(None)
def on_action_undo(self):
logger.debug('Undo: %s' % self.undo_stack.undoText())
@ -270,8 +271,6 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin):
self.scene.add_queued_items()
def on_loading_finished(self, filename, errors):
if filename:
self.filename = filename
if errors:
QtWidgets.QMessageBox.warning(
self,
@ -279,6 +278,7 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin):
('<p>Problem loading file %s</p>'
'<p>Not accessible or not a proper bee file</p>') % filename)
else:
self.filename = filename
self.scene.add_queued_items()
self.on_action_fit_scene()
@ -305,15 +305,15 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin):
self.filename = filename
def on_saving_finished(self, filename, errors):
if filename:
self.filename = filename
self.undo_stack.setClean()
else:
if errors:
QtWidgets.QMessageBox.warning(
self,
'Problem saving file',
('<p>Problem saving file %s</p>'
'<p>File/directory not accessible</p>') % filename)
else:
self.filename = filename
self.undo_stack.setClean()
def do_save(self, filename, create_new):
if not filename.endswith('.bee'):

View file

@ -2,3 +2,4 @@ coverage
flake8
httpretty
pytest
git+https://github.com/pytest-dev/pytest-qt.git

View file

@ -5,7 +5,6 @@ from PyQt6 import QtWidgets
from beeref.actions import ActionsMixin
from beeref.actions.menu_structure import MENU_SEPARATOR
from ..base import BeeTestCase
class FooWidget(QtWidgets.QWidget, ActionsMixin):
@ -23,226 +22,265 @@ class FooWidget(QtWidgets.QWidget, ActionsMixin):
pass
class ActionsMixinTestCase(BeeTestCase):
@patch('PyQt6.QtGui.QAction.triggered')
@patch('PyQt6.QtGui.QAction.toggled')
@patch('beeref.actions.mixin.menu_structure')
@patch('beeref.actions.mixin.actions')
def test_create_actions(
actions_mock, menu_mock, toggle_mock, trigger_mock, qapp):
widget = FooWidget()
actions_mock.__iter__.return_value = [{
'id': 'foo',
'text': '&Foo',
'shortcuts': ['Ctrl+F'],
'callback': 'on_foo',
}]
def setUp(self):
menu_patcher = patch('beeref.actions.mixin.menu_structure')
self.menu_mock = menu_patcher.start()
self.addCleanup(menu_patcher.stop)
actions_patcher = patch('beeref.actions.mixin.actions')
self.actions_mock = actions_patcher.start()
self.addCleanup(actions_patcher.stop)
self.widget = FooWidget()
menu_mock.__iter__.return_value = ['foo']
widget.build_menu_and_actions()
trigger_mock.connect.assert_called_once_with(widget.on_foo)
toggle_mock.connect.assert_not_called()
@patch('PyQt6.QtGui.QAction.triggered')
@patch('PyQt6.QtGui.QAction.toggled')
def test_create_actions(self, toggle_mock, trigger_mock):
self.actions_mock.__iter__.return_value = [{
'id': 'foo',
'text': '&Foo',
'shortcuts': ['Ctrl+F'],
'callback': 'on_foo',
}]
assert len(widget.actions()) == 1
qaction = widget.actions()[0]
assert qaction.text() == '&Foo'
assert qaction.shortcut() == 'Ctrl+F'
assert qaction.isEnabled() is True
assert widget.bee_actions['foo'] == qaction
self.menu_mock.__iter__.return_value = ['foo']
self.widget.build_menu_and_actions()
trigger_mock.connect.assert_called_once_with(self.widget.on_foo)
toggle_mock.connect.assert_not_called()
assert len(self.widget.actions()) == 1
qaction = self.widget.actions()[0]
assert qaction.text() == '&Foo'
assert qaction.shortcut() == 'Ctrl+F'
assert qaction.isEnabled() is True
assert self.widget.bee_actions['foo'] == qaction
@patch('PyQt6.QtGui.QAction.triggered')
@patch('PyQt6.QtGui.QAction.toggled')
@patch('beeref.actions.mixin.menu_structure')
@patch('beeref.actions.mixin.actions')
def test_create_actions_checkable(
actions_mock, menu_mock, toggle_mock, trigger_mock, qapp):
widget = FooWidget()
actions_mock.__iter__.return_value = [{
'id': 'foo',
'text': '&Foo',
'checkable': True,
'callback': 'on_foo',
}]
@patch('PyQt6.QtGui.QAction.triggered')
@patch('PyQt6.QtGui.QAction.toggled')
def test_create_actions_checkable(self, toggle_mock, trigger_mock):
self.actions_mock.__iter__.return_value = [{
'id': 'foo',
'text': '&Foo',
'checkable': True,
'callback': 'on_foo',
}]
menu_mock.__iter__.return_value = ['foo']
widget.build_menu_and_actions()
trigger_mock.connect.assert_not_called()
toggle_mock.connect.assert_called_once_with(widget.on_foo)
self.menu_mock.__iter__.return_value = ['foo']
self.widget.build_menu_and_actions()
trigger_mock.connect.assert_not_called()
toggle_mock.connect.assert_called_once_with(self.widget.on_foo)
assert len(widget.actions()) == 1
qaction = widget.actions()[0]
assert qaction.text() == '&Foo'
assert qaction.isEnabled() is True
assert qaction.isChecked() is False
assert widget.bee_actions['foo'] == qaction
assert len(self.widget.actions()) == 1
qaction = self.widget.actions()[0]
assert qaction.text() == '&Foo'
assert qaction.isEnabled() is True
assert qaction.isChecked() is False
assert self.widget.bee_actions['foo'] == qaction
@patch.object(FooWidget, 'on_foo')
@patch.object(FooWidget, 'settings')
@patch('PyQt6.QtGui.QAction.toggled')
def test_create_actions_checkable_with_settings(
self, toggle_mock, settings_mock, callback_mock):
self.actions_mock.__iter__.return_value = [{
'id': 'foo',
'text': '&Foo',
'checkable': True,
'callback': 'on_foo',
'settings': 'foo/bar',
}]
@patch.object(FooWidget, 'on_foo')
@patch.object(FooWidget, 'settings')
@patch('PyQt6.QtGui.QAction.toggled')
@patch('beeref.actions.mixin.menu_structure')
@patch('beeref.actions.mixin.actions')
def test_create_actions_checkable_with_settings(
actions_mock, menu_mock, toggle_mock, settings_mock, callback_mock,
qapp):
widget = FooWidget()
actions_mock.__iter__.return_value = [{
'id': 'foo',
'text': '&Foo',
'checkable': True,
'callback': 'on_foo',
'settings': 'foo/bar',
}]
self.menu_mock.__iter__.return_value = ['foo']
settings_mock.value.return_value = True
self.widget.build_menu_and_actions()
settings_mock.value.assert_called_once_with(
'foo/bar', False, type=bool)
qaction = self.widget.actions()[0]
assert qaction.isChecked() is True
assert toggle_mock.connect.call_count == 2
callback_mock.assert_called_once_with(True)
menu_mock.__iter__.return_value = ['foo']
settings_mock.value.return_value = True
widget.build_menu_and_actions()
settings_mock.value.assert_called_once_with(
'foo/bar', False, type=bool)
qaction = widget.actions()[0]
assert qaction.isChecked() is True
assert toggle_mock.connect.call_count == 2
callback_mock.assert_called_once_with(True)
def test_create_actions_with_group(self):
self.actions_mock.__iter__.return_value = [{
@patch('beeref.actions.mixin.menu_structure')
@patch('beeref.actions.mixin.actions')
def test_create_actions_with_group(actions_mock, menu_mock, qapp):
widget = FooWidget()
actions_mock.__iter__.return_value = [{
'id': 'foo',
'text': '&Foo',
'callback': 'on_foo',
'group': 'bar',
}]
menu_mock.__iter__.return_value = ['foo']
widget.build_menu_and_actions()
assert len(widget.actions()) == 1
qaction = widget.actions()[0]
assert widget.bee_actiongroups['bar'] == [qaction]
@patch('beeref.actions.mixin.menu_structure')
@patch('beeref.actions.mixin.actions')
def test_build_menu_and_actions_with_actions(actions_mock, menu_mock, qapp):
widget = FooWidget()
actions_mock.__iter__.return_value = [{
'id': 'foo',
'text': '&Foo',
'callback': 'on_foo',
'group': 'bar',
}]
menu_mock.__iter__.return_value = ['foo']
with patch('PyQt6.QtWidgets.QMenu.addAction') as add_mock:
widget.build_menu_and_actions()
assert isinstance(widget.context_menu, QtWidgets.QMenu)
add_mock.assert_called_once_with(widget.bee_actions['foo'])
@patch('beeref.actions.mixin.menu_structure')
@patch('beeref.actions.mixin.actions')
def test_build_menu_and_actions_with_separator(actions_mock, menu_mock, qapp):
widget = FooWidget()
menu_mock.__iter__.return_value = [MENU_SEPARATOR]
with patch('PyQt6.QtWidgets.QMenu.addSeparator') as sep_mock:
widget.build_menu_and_actions()
assert isinstance(widget.context_menu, QtWidgets.QMenu)
sep_mock.assert_called_once_with()
@patch('beeref.actions.mixin.menu_structure')
@patch('beeref.actions.mixin.actions')
def test_build_menu_and_actions_with_submenu(actions_mock, menu_mock, qapp):
widget = FooWidget()
actions_mock.__iter__.return_value = [{
'id': 'foo',
'text': '&Foo',
'callback': 'on_foo',
'group': 'bar',
}]
menu_mock.__iter__.return_value = [
{'menu': '&Bar', 'items': ['foo']}]
with patch('PyQt6.QtWidgets.QMenu.addAction') as add_mock:
with patch('PyQt6.QtWidgets.QMenu.addMenu') as addmenu_mock:
addmenu_mock.return_value = QtWidgets.QMenu()
widget.build_menu_and_actions()
assert isinstance(widget.context_menu, QtWidgets.QMenu)
addmenu_mock.assert_called_once_with('&Bar')
add_mock.assert_called_once_with(widget.bee_actions['foo'])
@patch('beeref.actions.mixin.menu_structure')
@patch('beeref.actions.mixin.actions')
def test_actiongroup_set_enabled(actions_mock, menu_mock, qapp):
widget = FooWidget()
actions_mock.__iter__.return_value = [
{
'id': 'foo',
'text': '&Foo',
'callback': 'on_foo',
'group': 'bar',
}]
self.menu_mock.__iter__.return_value = ['foo']
self.widget.build_menu_and_actions()
assert len(self.widget.actions()) == 1
qaction = self.widget.actions()[0]
assert self.widget.bee_actiongroups['bar'] == [qaction]
'group': 'g1',
},
{
'id': 'bar',
'text': '&Bar',
'callback': 'on_foo',
'group': 'g2',
},
]
def test_build_menu_and_actions_with_actions(self):
self.actions_mock.__iter__.return_value = [{
menu_mock.__iter__.return_value = ['foo']
widget.build_menu_and_actions()
widget.actiongroup_set_enabled('g1', True)
assert widget.bee_actions['foo'].isEnabled() is True
assert widget.bee_actions['bar'].isEnabled() is False
@patch('beeref.actions.mixin.menu_structure')
@patch('beeref.actions.mixin.actions')
def test_build_menu_and_actions_disables_actiongroups(
actions_mock, menu_mock, qapp):
widget = FooWidget()
widget.scene.has_selection.return_value = False
actions_mock.__iter__.return_value = [
{
'id': 'foo',
'text': '&Foo',
'callback': 'on_foo',
'group': 'bar',
}]
self.menu_mock.__iter__.return_value = ['foo']
with patch('PyQt6.QtWidgets.QMenu.addAction') as add_mock:
self.widget.build_menu_and_actions()
assert isinstance(self.widget.context_menu, QtWidgets.QMenu)
add_mock.assert_called_once_with(self.widget.bee_actions['foo'])
'group': 'active_when_selection',
},
]
def test_build_menu_and_actions_with_separator(self):
self.menu_mock.__iter__.return_value = [MENU_SEPARATOR]
with patch('PyQt6.QtWidgets.QMenu.addSeparator') as sep_mock:
self.widget.build_menu_and_actions()
assert isinstance(self.widget.context_menu, QtWidgets.QMenu)
sep_mock.assert_called_once_with()
menu_mock.__iter__.return_value = ['foo']
widget.build_menu_and_actions()
qaction = widget.actions()[0]
assert qaction.isEnabled() is False
def test_build_menu_and_actions_with_submenu(self):
self.actions_mock.__iter__.return_value = [{
'id': 'foo',
'text': '&Foo',
'callback': 'on_foo',
'group': 'bar',
}]
self.menu_mock.__iter__.return_value = [
{'menu': '&Bar', 'items': ['foo']}]
with patch('PyQt6.QtWidgets.QMenu.addAction') as add_mock:
with patch('PyQt6.QtWidgets.QMenu.addMenu') as addmenu_mock:
addmenu_mock.return_value = QtWidgets.QMenu()
self.widget.build_menu_and_actions()
assert isinstance(self.widget.context_menu, QtWidgets.QMenu)
addmenu_mock.assert_called_once_with('&Bar')
add_mock.assert_called_once_with(
self.widget.bee_actions['foo'])
def test_actiongroup_set_enabled(self):
self.actions_mock.__iter__.return_value = [
{
'id': 'foo',
'text': '&Foo',
'callback': 'on_foo',
'group': 'g1',
},
{
'id': 'bar',
'text': '&Bar',
'callback': 'on_foo',
'group': 'g2',
},
]
@patch('beeref.config.BeeSettings.get_recent_files')
@patch('PyQt6.QtGui.QAction.triggered')
@patch('beeref.actions.mixin.menu_structure')
@patch('beeref.actions.mixin.actions')
def test_create_recent_files(
actions_mock, menu_mock, triggered_mock, files_mock, qapp):
widget = FooWidget()
files_mock.return_value = [
os.path.abspath(f'{i}.bee') for i in range(15)]
menu_mock.__iter__.return_value = [{
'menu': 'Open &Recent',
'items': '_build_recent_files',
}]
self.menu_mock.__iter__.return_value = ['foo']
self.widget.build_menu_and_actions()
self.widget.actiongroup_set_enabled('g1', True)
assert self.widget.bee_actions['foo'].isEnabled() is True
assert self.widget.bee_actions['bar'].isEnabled() is False
widget.build_menu_and_actions()
triggered_mock.connect.assert_called()
assert len(widget.actions()) == 15
qaction1 = widget.actions()[0]
assert qaction1.text() == '0.bee'
assert qaction1.shortcut() == 'Ctrl+1'
assert qaction1.isEnabled() is True
assert widget.bee_actions['recent_files_0'] == qaction1
qaction10 = widget.actions()[9]
assert qaction10.text() == '9.bee'
assert qaction10.shortcut() == 'Ctrl+0'
assert qaction10.isEnabled() is True
assert widget.bee_actions['recent_files_9'] == qaction10
qaction15 = widget.actions()[-1]
assert qaction15.text() == '14.bee'
assert qaction15.shortcut() == ''
assert qaction15.isEnabled() is True
assert widget.bee_actions['recent_files_14'] == qaction15
def test_build_menu_and_actions_disables_actiongroups(self):
self.widget.scene.has_selection.return_value = False
self.actions_mock.__iter__.return_value = [
{
'id': 'foo',
'text': '&Foo',
'callback': 'on_foo',
'group': 'active_when_selection',
},
]
self.menu_mock.__iter__.return_value = ['foo']
self.widget.build_menu_and_actions()
qaction = self.widget.actions()[0]
assert qaction.isEnabled() is False
@patch('beeref.config.BeeSettings.get_recent_files')
@patch('PyQt6.QtGui.QAction.triggered')
@patch('beeref.actions.mixin.menu_structure')
@patch('beeref.actions.mixin.actions')
def test_update_recent_files(
actions_mock, menu_mock, triggered_mock, files_mock, qapp):
widget = FooWidget()
files_mock.return_value = [os.path.abspath('foo.bee')]
menu_mock.__iter__.return_value = [{
'menu': 'Open &Recent',
'items': '_build_recent_files',
}]
@patch('beeref.config.BeeSettings.get_recent_files')
@patch('PyQt6.QtGui.QAction.triggered')
def test_create_recent_files(self, triggered_mock, files_mock):
files_mock.return_value = [
os.path.abspath(f'{i}.bee') for i in range(15)]
self.menu_mock.__iter__.return_value = [{
'menu': 'Open &Recent',
'items': '_build_recent_files',
}]
widget.build_menu_and_actions()
triggered_mock.connect.reset_mock()
assert len(widget.actions()) == 1
qaction1 = widget.actions()[0]
assert qaction1.text() == 'foo.bee'
self.widget.build_menu_and_actions()
triggered_mock.connect.assert_called()
assert len(self.widget.actions()) == 15
qaction1 = self.widget.actions()[0]
assert qaction1.text() == '0.bee'
assert qaction1.shortcut() == 'Ctrl+1'
assert qaction1.isEnabled() is True
assert self.widget.bee_actions['recent_files_0'] == qaction1
qaction10 = self.widget.actions()[9]
assert qaction10.text() == '9.bee'
assert qaction10.shortcut() == 'Ctrl+0'
assert qaction10.isEnabled() is True
assert self.widget.bee_actions['recent_files_9'] == qaction10
qaction15 = self.widget.actions()[-1]
assert qaction15.text() == '14.bee'
assert qaction15.shortcut() == ''
assert qaction15.isEnabled() is True
assert self.widget.bee_actions['recent_files_14'] == qaction15
files_mock.return_value = [os.path.abspath('bar.bee')]
widget.update_menu_and_actions()
triggered_mock.connect.assert_called()
assert len(widget.actions()) == 1
qaction1 = widget.actions()[0]
assert qaction1.text() == 'bar.bee'
@patch('beeref.config.BeeSettings.get_recent_files')
@patch('PyQt6.QtGui.QAction.triggered')
def test_update_recent_files(self, triggered_mock, files_mock):
files_mock.return_value = [os.path.abspath('foo.bee')]
self.menu_mock.__iter__.return_value = [{
'menu': 'Open &Recent',
'items': '_build_recent_files',
}]
self.widget.build_menu_and_actions()
triggered_mock.connect.reset_mock()
assert len(self.widget.actions()) == 1
qaction1 = self.widget.actions()[0]
assert qaction1.text() == 'foo.bee'
files_mock.return_value = [os.path.abspath('bar.bee')]
self.widget.update_menu_and_actions()
triggered_mock.connect.assert_called()
assert len(self.widget.actions()) == 1
qaction1 = self.widget.actions()[0]
assert qaction1.text() == 'bar.bee'
def test_create_menubar(self):
self.widget.toplevel_menus = [QtWidgets.QMenu('Foo')]
menubar = self.widget.create_menubar()
assert isinstance(menubar, QtWidgets.QMenuBar)
assert len(menubar.actions()) == 1
def test_create_menubar(qapp):
widget = FooWidget()
widget.toplevel_menus = [QtWidgets.QMenu('Foo')]
menubar = widget.create_menubar()
assert isinstance(menubar, QtWidgets.QMenuBar)
assert len(menubar.actions()) == 1

View file

@ -1,43 +0,0 @@
import os.path
import tempfile
from unittest import mock, TestCase
from PyQt6 import QtWidgets
from beeref.config import BeeSettings
root = os.path.dirname(__file__)
imgfilename3x3 = os.path.join(root, 'assets', 'test3x3.png')
with open(imgfilename3x3, 'rb') as f:
imgdata3x3 = f.read()
class BeeTestCase(TestCase):
imgfilename3x3 = imgfilename3x3
imgdata3x3 = imgdata3x3
@classmethod
def setUpClass(cls):
cls._settings_dir = tempfile.TemporaryDirectory()
settings_dir_patcher = mock.patch(
'beeref.config.BeeSettings.get_settings_dir',
return_value=cls._settings_dir.name)
cls._settings_dir_mock = settings_dir_patcher.start()
inst = QtWidgets.QApplication.instance()
cls.app = inst if inst else QtWidgets.QApplication([])
@classmethod
def tearDownClass(cls):
cls._settings_dir_mock.stop()
cls._settings_dir.cleanup()
def tearDown(self):
BeeSettings().clear()
def queue2list(self, queue):
qlist = []
while not queue.empty():
qlist.append(queue.get())
return qlist

View file

@ -1,4 +1,8 @@
from unittest.mock import MagicMock
import os.path
import pytest
from unittest.mock import MagicMock, patch
from PyQt6 import QtGui, QtWidgets
def pytest_configure(config):
@ -10,3 +14,57 @@ def pytest_configure(config):
# logging configuration happens on module level
import logging.config
logging.config.dictConfig = MagicMock
@pytest.fixture(autouse=True)
def commandline_args():
config_patcher = patch('beeref.view.commandline_args')
config_mock = config_patcher.start()
config_mock.filename = None
yield config_mock
config_patcher.stop()
@pytest.fixture(autouse=True)
def settings(tmpdir):
from beeref.config import BeeSettings
dir_patcher = patch('beeref.config.BeeSettings.get_settings_dir',
return_value=tmpdir.dirname)
dir_patcher.start()
settings = BeeSettings()
yield settings
settings.clear()
dir_patcher.stop()
@pytest.fixture
def main_window(qtbot):
from beeref.__main__ import BeeRefMainWindow
app = QtWidgets.QApplication.instance()
main = BeeRefMainWindow(app)
qtbot.addWidget(main)
yield main
@pytest.fixture
def view(main_window):
yield main_window.view
@pytest.fixture
def imgfilename3x3():
root = os.path.dirname(__file__)
yield os.path.join(root, 'assets', 'test3x3.png')
@pytest.fixture
def imgdata3x3(imgfilename3x3):
with open(imgfilename3x3, 'rb') as f:
imgdata3x3 = f.read()
yield imgdata3x3
@pytest.fixture
def item():
from beeref.items import BeePixmapItem
yield BeePixmapItem(QtGui.QImage())

View file

@ -3,47 +3,48 @@ import httpretty
from PyQt6 import QtCore
from beeref.fileio.image import load_image
from ..base import BeeTestCase
class LoadImageTestCase(BeeTestCase):
def test_load_image_loads_from_filename(view, imgfilename3x3):
img, filename = load_image(imgfilename3x3)
assert img.isNull() is False
assert filename == imgfilename3x3
def test_loads_from_filename(self):
img, filename = load_image(self.imgfilename3x3)
assert img.isNull() is False
assert filename == self.imgfilename3x3
def test_loads_from_nonexisting_filename(self):
img, filename = load_image('foo.png')
assert img.isNull() is True
assert filename == 'foo.png'
def test_load_image_loads_from_nonexisting_filename(view, imgfilename3x3):
img, filename = load_image('foo.png')
assert img.isNull() is True
assert filename == 'foo.png'
def test_loads_from_existing_local_url(self):
url = QtCore.QUrl.fromLocalFile(self.imgfilename3x3)
img, filename = load_image(url)
assert img.isNull() is False
assert filename == self.imgfilename3x3
@httpretty.activate
def test_loads_from_existing_web_url(self):
url = 'http://example.com/foo.png'
httpretty.register_uri(
httpretty.GET,
url,
body=self.imgdata3x3,
)
img, filename = load_image(QtCore.QUrl(url))
assert img.isNull() is False
assert filename == url
def test_load_image_loads_from_existing_local_url(view, imgfilename3x3):
url = QtCore.QUrl.fromLocalFile(imgfilename3x3)
img, filename = load_image(url)
assert img.isNull() is False
assert filename == imgfilename3x3
@httpretty.activate
def test_loads_from_web_url_errors(self):
url = 'http://example.com/foo.png'
httpretty.register_uri(
httpretty.GET,
url,
status=500,
)
img, filename = load_image(QtCore.QUrl(url))
assert img.isNull() is True
assert filename == url
@httpretty.activate
def test_load_image_loads_from_existing_web_url(view, imgdata3x3):
url = 'http://example.com/foo.png'
httpretty.register_uri(
httpretty.GET,
url,
body=imgdata3x3,
)
img, filename = load_image(QtCore.QUrl(url))
assert img.isNull() is False
assert filename == url
@httpretty.activate
def test_load_image_loads_from_web_url_errors(view, imgfilename3x3):
url = 'http://example.com/foo.png'
httpretty.register_uri(
httpretty.GET,
url,
status=500,
)
img, filename = load_image(QtCore.QUrl(url))
assert img.isNull() is True
assert filename == url

View file

@ -6,8 +6,7 @@ from PyQt6 import QtCore
from beeref import fileio
from beeref import commands
from beeref.scene import BeeGraphicsScene
from ..base import BeeTestCase
from ..utils import queue2list
@patch('beeref.fileio.sql.SQLiteIO.write')
@ -26,62 +25,62 @@ def test_read_bee(read_mock):
read_mock.assert_called_once()
class LoadImagesTestCase(BeeTestCase):
def test_load_images_loads(view, imgfilename3x3):
view.scene.undo_stack = MagicMock()
worker = MagicMock(canceled=False)
fileio.load_images([imgfilename3x3],
QtCore.QPointF(5, 6), view.scene, worker)
worker.begin_processing.emit.assert_called_once_with(1)
worker.progress.emit.assert_called_once_with(0)
worker.finished.emit.assert_called_once_with('', [])
items = queue2list(view.scene.items_to_add)
assert len(items) == 1
item = items[0][0]
args = view.scene.undo_stack.push.call_args_list[0][0]
cmd = args[0]
assert isinstance(cmd, commands.InsertItems)
assert cmd.items == [item]
assert cmd.scene == view.scene
assert cmd.ignore_first_redo is True
assert item.pos() == QtCore.QPointF(3.5, 4.5)
def setUp(self):
self.scene = BeeGraphicsScene(MagicMock())
def test_loads(self):
worker = MagicMock(canceled=False)
fileio.load_images([self.imgfilename3x3],
QtCore.QPointF(5, 6), self.scene, worker)
worker.begin_processing.emit.assert_called_once_with(1)
worker.progress.emit.assert_called_once_with(0)
worker.finished.emit.assert_called_once_with('', [])
items = self.queue2list(self.scene.items_to_add)
assert len(items) == 1
item = items[0][0]
args = self.scene.undo_stack.push.call_args_list[0][0]
cmd = args[0]
assert isinstance(cmd, commands.InsertItems)
assert cmd.items == [item]
assert cmd.scene == self.scene
assert cmd.ignore_first_redo is True
assert item.pos() == QtCore.QPointF(3.5, 4.5)
def test_load_images_canceled(view, imgfilename3x3):
view.scene.undo_stack = MagicMock()
worker = MagicMock(canceled=True)
fileio.load_images([imgfilename3x3, imgfilename3x3],
QtCore.QPointF(5, 6), view.scene, worker)
worker.begin_processing.emit.assert_called_once_with(2)
worker.progress.emit.assert_called_once_with(0)
worker.finished.emit.assert_called_once_with('', [])
items = queue2list(view.scene.items_to_add)
assert len(items) == 1
item = items[0][0]
args = view.scene.undo_stack.push.call_args_list[0][0]
cmd = args[0]
assert isinstance(cmd, commands.InsertItems)
assert cmd.items == [item]
assert cmd.scene == view.scene
assert cmd.ignore_first_redo is True
assert item.pos() == QtCore.QPointF(3.5, 4.5)
def test_canceled(self):
worker = MagicMock(canceled=True)
fileio.load_images([self.imgfilename3x3, self.imgfilename3x3],
QtCore.QPointF(5, 6), self.scene, worker)
worker.begin_processing.emit.assert_called_once_with(2)
worker.progress.emit.assert_called_once_with(0)
worker.finished.emit.assert_called_once_with('', [])
items = self.queue2list(self.scene.items_to_add)
assert len(items) == 1
item = items[0][0]
args = self.scene.undo_stack.push.call_args_list[0][0]
cmd = args[0]
assert isinstance(cmd, commands.InsertItems)
assert cmd.items == [item]
assert cmd.scene == self.scene
assert cmd.ignore_first_redo is True
assert item.pos() == QtCore.QPointF(3.5, 4.5)
def test_error(self):
worker = MagicMock(canceled=False)
fileio.load_images(['foo.jpg', self.imgfilename3x3],
QtCore.QPointF(5, 6), self.scene, worker)
worker.begin_processing.emit.assert_called_once_with(2)
worker.progress.emit.assert_any_call(0)
worker.progress.emit.assert_any_call(1)
worker.finished.emit.assert_called_once_with('', ['foo.jpg'])
items = self.queue2list(self.scene.items_to_add)
assert len(items) == 1
item = items[0][0]
args = self.scene.undo_stack.push.call_args_list[0][0]
cmd = args[0]
assert isinstance(cmd, commands.InsertItems)
assert cmd.items == [item]
assert cmd.scene == self.scene
assert cmd.ignore_first_redo is True
assert item.pos() == QtCore.QPointF(3.5, 4.5)
def test_load_images_error(view, imgfilename3x3):
view.scene.undo_stack = MagicMock()
worker = MagicMock(canceled=False)
fileio.load_images(['foo.jpg', imgfilename3x3],
QtCore.QPointF(5, 6), view.scene, worker)
worker.begin_processing.emit.assert_called_once_with(2)
worker.progress.emit.assert_any_call(0)
worker.progress.emit.assert_any_call(1)
worker.finished.emit.assert_called_once_with('', ['foo.jpg'])
items = queue2list(view.scene.items_to_add)
assert len(items) == 1
item = items[0][0]
args = view.scene.undo_stack.push.call_args_list[0][0]
cmd = args[0]
assert isinstance(cmd, commands.InsertItems)
assert cmd.items == [item]
assert cmd.scene == view.scene
assert cmd.ignore_first_redo is True
assert item.pos() == QtCore.QPointF(3.5, 4.5)

View file

@ -1,5 +1,4 @@
import os.path
import tempfile
from unittest.mock import MagicMock, patch
from PyQt6 import QtGui
@ -8,314 +7,314 @@ import pytest
from beeref.fileio.errors import BeeFileIOError
from beeref.fileio.sql import SQLiteIO
from beeref.items import BeePixmapItem
from beeref.scene import BeeGraphicsScene
from ..base import BeeTestCase
class SQLiteIOTestCase(BeeTestCase):
def test_sqliteio_ẁrite_meta_application_id():
io = SQLiteIO(':memory:', MagicMock(), create_new=True)
io.write_meta()
result = io.fetchone('PRAGMA application_id')
assert result[0] == SQLiteIO.APPLICATION_ID
def setUp(self):
self.scene_mock = MagicMock()
self.io = SQLiteIO(':memory:', self.scene_mock, create_new=True)
def test_ẁrite_meta_application_id(self):
self.io.write_meta()
result = self.io.fetchone('PRAGMA application_id')
assert result[0] == SQLiteIO.APPLICATION_ID
def test_sqliteio_ẁrite_meta_user_version():
io = SQLiteIO(':memory:', MagicMock(), create_new=True)
io.write_meta()
result = io.fetchone('PRAGMA user_version')
assert result[0] == SQLiteIO.USER_VERSION
def test_ẁrite_meta_user_version(self):
self.io.write_meta()
result = self.io.fetchone('PRAGMA user_version')
assert result[0] == SQLiteIO.USER_VERSION
def test_ẁrite_meta_foreign_keys(self):
self.io.write_meta()
result = self.io.fetchone('PRAGMA foreign_keys')
assert result[0] == 1
def test_sqliteio_ẁrite_meta_foreign_keys():
io = SQLiteIO(':memory:', MagicMock(), create_new=True)
io.write_meta()
result = io.fetchone('PRAGMA foreign_keys')
assert result[0] == 1
def test_create_schema_on_new_when_create_new(self):
self.io.create_schema_on_new()
result = self.io.fetchone(
'SELECT COUNT(*) FROM sqlite_master '
'WHERE type="table" AND name NOT LIKE "sqlite_%"')
assert result[0] == 2
self.scene_mock.clear_save_ids.assert_called_once()
def test_create_schema_on_new_when_not_create_new(self):
self.io.create_new = False
self.io.create_schema_on_new()
result = self.io.fetchone(
'SELECT COUNT(*) FROM sqlite_master '
'WHERE type="table" AND name NOT LIKE "sqlite_%"')
assert result[0] == 0
self.scene_mock.clear_save_ids.assert_not_called()
def test_sqliteio_create_schema_on_new_when_create_new():
scene_mock = MagicMock()
io = SQLiteIO(':memory:', scene_mock, create_new=True)
io.create_schema_on_new()
result = io.fetchone(
'SELECT COUNT(*) FROM sqlite_master '
'WHERE type="table" AND name NOT LIKE "sqlite_%"')
assert result[0] == 2
scene_mock.clear_save_ids.assert_called_once()
def test_readonly_doesnt_allow_write(self):
scene = BeeGraphicsScene(None)
with tempfile.TemporaryDirectory() as dirname:
fname = os.path.join(dirname, 'test.bee')
with open(fname, 'w') as f:
f.write('foobar')
io = SQLiteIO(fname, scene, readonly=True)
with pytest.raises(BeeFileIOError) as exinfo:
def test_sqliteio_create_schema_on_new_when_not_create_new():
scene_mock = MagicMock()
io = SQLiteIO(':memory:', scene_mock, create_new=False)
io.create_schema_on_new()
result = io.fetchone(
'SELECT COUNT(*) FROM sqlite_master '
'WHERE type="table" AND name NOT LIKE "sqlite_%"')
assert result[0] == 0
scene_mock.clear_save_ids.assert_not_called()
def test_sqliteio_readonly_doesnt_allow_write(view, tmpdir):
fname = os.path.join(tmpdir, 'test.bee')
with open(fname, 'w') as f:
f.write('foobar')
io = SQLiteIO(fname, view.scene, readonly=True)
with pytest.raises(BeeFileIOError) as exinfo:
io.write()
assert exinfo.value.filename == fname
with open(fname, 'r') as f:
f.read() == 'foobar'
def test_sqliteio_write_calls_create_schema_on_new(view):
io = SQLiteIO(':memory:', view.scene, create_new=True)
with patch.object(io, 'create_schema_on_new') as crmock:
with patch.object(io, 'fetchall'):
with patch.object(io, 'exmany'):
io.write()
assert exinfo.value.filename == fname
with open(fname, 'r') as f:
f.read() == 'foobar'
crmock.assert_called_once()
class SQLiteIOWriteTestCase(BeeTestCase):
def setUp(self):
self.scene = BeeGraphicsScene(None)
self.io = SQLiteIO(':memory:', self.scene, create_new=True)
def test_calls_create_schema_on_new(self):
with patch.object(self.io, 'create_schema_on_new') as crmock:
with patch.object(self.io, 'fetchall'):
with patch.object(self.io, 'exmany'):
self.io.write()
crmock.assert_called_once()
def test_calls_write_meta(self):
with patch.object(self.io, 'write_meta') as metamock:
with patch.object(self.io, 'fetchall'):
with patch.object(self.io, 'exmany'):
self.io.write()
metamock.assert_called_once()
def test_inserts_new_item(self):
item = BeePixmapItem(QtGui.QImage(), filename='bee.jpg')
self.scene.addItem(item)
item.setScale(1.3)
item.setPos(44, 55)
item.setZValue(0.22)
item.setRotation(33)
item.do_flip()
item.pixmap_to_bytes = MagicMock(return_value=b'abc')
self.io.write()
assert item.save_id == 1
result = self.io.fetchone(
'SELECT x, y, z, scale, rotation, flip, filename, type, '
'sqlar.data, sqlar.name '
'FROM items '
'INNER JOIN sqlar on sqlar.item_id = items.id')
assert result[0] == 44.0
assert result[1] == 55.0
assert result[2] == 0.22
assert result[3] == 1.3
assert result[4] == 33
assert result[5] == -1
assert result[6] == 'bee.jpg'
assert result[7] == 'pixmap'
assert result[8] == b'abc'
assert result[9] == '0001-bee.png'
def test_inserts_new_item_without_filename(self):
item = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item)
self.io.write()
assert item.save_id == 1
result = self.io.fetchone(
'SELECT filename, sqlar.name FROM items '
'INNER JOIN sqlar on sqlar.item_id = items.id')
assert result[0] is None
assert result[1] == '0001.png'
def test_updates_existing_item(self):
item = BeePixmapItem(QtGui.QImage(), filename='bee.png')
self.scene.addItem(item)
item.setScale(1.3)
item.setPos(44, 55)
item.setZValue(0.22)
item.setRotation(33)
item.save_id = 1
item.pixmap_to_bytes = MagicMock(return_value=b'abc')
self.io.write()
item.setScale(0.7)
item.setPos(20, 30)
item.setZValue(0.33)
item.setRotation(100)
item.do_flip()
item.filename = 'new.png'
item.pixmap_to_bytes.return_value = b'updated'
self.io.create_new = False
self.io.write()
assert self.io.fetchone('SELECT COUNT(*) from items') == (1,)
result = self.io.fetchone(
'SELECT x, y, z, scale, rotation, flip, filename, sqlar.data '
'FROM items '
'INNER JOIN sqlar on sqlar.item_id = items.id')
assert result[0] == 20
assert result[1] == 30
assert result[2] == 0.33
assert result[3] == 0.7
assert result[4] == 100
assert result[5] == -1
assert result[6] == 'new.png'
assert result[7] == b'abc'
def test_removes_nonexisting_item(self):
item = BeePixmapItem(QtGui.QImage(), filename='bee.png')
item.setScale(1.3)
item.setPos(44, 55)
self.scene.addItem(item)
self.io.write()
self.scene.removeItem(item)
self.io.create_new = False
self.io.write()
assert self.io.fetchone('SELECT COUNT(*) from items') == (0,)
assert self.io.fetchone('SELECT COUNT(*) from sqlar') == (0,)
def test_update_recovers_from_borked_file(self):
item = BeePixmapItem(QtGui.QImage(), filename='bee.png')
self.scene.addItem(item)
with tempfile.TemporaryDirectory() as dirname:
fname = os.path.join(dirname, 'test.bee')
with open(fname, 'w') as f:
f.write('foobar')
io = SQLiteIO(fname, self.scene, create_new=False)
io.write()
result = io.fetchone('SELECT COUNT(*) FROM items')
assert result[0] == 1
def test_updates_progress(self):
worker = MagicMock(canceled=False)
io = SQLiteIO(':memory:', self.scene, create_new=True,
worker=worker)
item = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item)
io.write()
worker.begin_processing.emit.assert_called_once_with(1)
worker.progress.emit.assert_called_once_with(0)
worker.finished.emit.assert_called_once_with(':memory:', [])
def test_canceled(self):
worker = MagicMock(canceled=True)
io = SQLiteIO(':memory:', self.scene, create_new=True,
worker=worker)
item = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item)
item = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item)
io.write()
worker.begin_processing.emit.assert_called_once_with(2)
worker.progress.emit.assert_called_once_with(0)
worker.finished.emit.assert_called_once_with(':memory:', [])
def test_sqliteio_write_calls_write_meta(view):
io = SQLiteIO(':memory:', view.scene, create_new=True)
with patch.object(io, 'write_meta') as metamock:
with patch.object(io, 'fetchall'):
with patch.object(io, 'exmany'):
io.write()
metamock.assert_called_once()
class SQLiteIOReadTestCase(BeeTestCase):
def test_sqliteio_write_inserts_new_item(view):
item = BeePixmapItem(QtGui.QImage(), filename='bee.jpg')
view.scene.addItem(item)
item.setScale(1.3)
item.setPos(44, 55)
item.setZValue(0.22)
item.setRotation(33)
item.do_flip()
item.pixmap_to_bytes = MagicMock(return_value=b'abc')
io = SQLiteIO(':memory:', view.scene, create_new=True)
io.write()
def setUp(self):
self.scene = BeeGraphicsScene(None)
assert item.save_id == 1
result = io.fetchone(
'SELECT x, y, z, scale, rotation, flip, filename, type, '
'sqlar.data, sqlar.name '
'FROM items '
'INNER JOIN sqlar on sqlar.item_id = items.id')
assert result[0] == 44.0
assert result[1] == 55.0
assert result[2] == 0.22
assert result[3] == 1.3
assert result[4] == 33
assert result[5] == -1
assert result[6] == 'bee.jpg'
assert result[7] == 'pixmap'
assert result[8] == b'abc'
assert result[9] == '0001-bee.png'
def test_reads_readonly(self):
with tempfile.TemporaryDirectory() as dirname:
fname = os.path.join(dirname, 'test.bee')
io = SQLiteIO(fname, self.scene, create_new=True)
io.create_schema_on_new()
io.ex('INSERT INTO items '
'(type, x, y, z, scale, rotation, flip, filename) '
'VALUES (?, ?, ?, ?, ?, ?, ?, ?) ',
('pixmap', 22.2, 33.3, 0.22, 3.4, 45, -1, 'bee.png'))
io.ex('INSERT INTO sqlar (item_id, data) VALUES (?, ?)',
(1, self.imgdata3x3))
io.connection.commit()
del(io)
io = SQLiteIO(fname, self.scene, readonly=True)
io.read()
item, selected = self.scene.items_to_add.get()
assert selected is False
assert item.save_id == 1
assert item.pos().x() == 22.2
assert item.pos().y() == 33.3
assert item.zValue() == 0.22
assert item.scale() == 3.4
assert item.rotation() == 45
assert item.flip() == -1
assert item.filename == 'bee.png'
assert item.width == 3
assert item.height == 3
assert self.scene.items_to_add.empty() is True
def test_sqliteio_write_inserts_new_item_without_filename(view, item):
view.scene.addItem(item)
io = SQLiteIO(':memory:', view.scene, create_new=True)
io.write()
def test_updates_progress(self):
worker = MagicMock(canceled=False)
io = SQLiteIO(':memory:', self.scene, create_new=True,
worker=worker)
assert item.save_id == 1
result = io.fetchone(
'SELECT filename, sqlar.name FROM items '
'INNER JOIN sqlar on sqlar.item_id = items.id')
assert result[0] is None
assert result[1] == '0001.png'
io.create_schema_on_new()
io.ex('INSERT INTO items (type, x, y, z, scale, filename) '
'VALUES (?, ?, ?, ?, ?, ?) ',
('pixmap', 0, 0, 0, 1, 'bee.png'))
io.ex('INSERT INTO sqlar (item_id, data) VALUES (?, ?)', (1, b''))
io.connection.commit()
def test_sqliteio_write_updates_existing_item(view):
item = BeePixmapItem(QtGui.QImage(), filename='bee.png')
view.scene.addItem(item)
item.setScale(1.3)
item.setPos(44, 55)
item.setZValue(0.22)
item.setRotation(33)
item.save_id = 1
item.pixmap_to_bytes = MagicMock(return_value=b'abc')
io = SQLiteIO(':memory:', view.scene, create_new=True)
io.write()
item.setScale(0.7)
item.setPos(20, 30)
item.setZValue(0.33)
item.setRotation(100)
item.do_flip()
item.filename = 'new.png'
item.pixmap_to_bytes.return_value = b'updated'
io.create_new = False
io.write()
assert io.fetchone('SELECT COUNT(*) from items') == (1,)
result = io.fetchone(
'SELECT x, y, z, scale, rotation, flip, filename, sqlar.data '
'FROM items '
'INNER JOIN sqlar on sqlar.item_id = items.id')
assert result[0] == 20
assert result[1] == 30
assert result[2] == 0.33
assert result[3] == 0.7
assert result[4] == 100
assert result[5] == -1
assert result[6] == 'new.png'
assert result[7] == b'abc'
def test_sqliteio_write_removes_nonexisting_item(view):
item = BeePixmapItem(QtGui.QImage(), filename='bee.png')
item.setScale(1.3)
item.setPos(44, 55)
view.scene.addItem(item)
io = SQLiteIO(':memory:', view.scene, create_new=True)
io.write()
view.scene.removeItem(item)
io.create_new = False
io.write()
assert io.fetchone('SELECT COUNT(*) from items') == (0,)
assert io.fetchone('SELECT COUNT(*) from sqlar') == (0,)
def test_sqliteio_write_update_recovers_from_borked_file(view, tmpdir):
item = BeePixmapItem(QtGui.QImage(), filename='bee.png')
view.scene.addItem(item)
fname = os.path.join(tmpdir, 'test.bee')
with open(fname, 'w') as f:
f.write('foobar')
io = SQLiteIO(fname, view.scene, create_new=False)
io.write()
result = io.fetchone('SELECT COUNT(*) FROM items')
assert result[0] == 1
def test_sqliteio_write_updates_progress(view):
worker = MagicMock(canceled=False)
io = SQLiteIO(':memory:', view.scene, create_new=True, worker=worker)
item = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item)
io.write()
worker.begin_processing.emit.assert_called_once_with(1)
worker.progress.emit.assert_called_once_with(0)
worker.finished.emit.assert_called_once_with(':memory:', [])
def test_sqliteio_write_canceled(view):
worker = MagicMock(canceled=True)
io = SQLiteIO(':memory:', view.scene, create_new=True, worker=worker)
item = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item)
item = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item)
io.write()
worker.begin_processing.emit.assert_called_once_with(2)
worker.progress.emit.assert_called_once_with(0)
worker.finished.emit.assert_called_once_with(':memory:', [])
def test_sqliteio_read_reads_readonly(view, tmpdir, imgdata3x3):
fname = os.path.join(tmpdir, 'test.bee')
io = SQLiteIO(fname, view.scene, create_new=True)
io.create_schema_on_new()
io.ex('INSERT INTO items '
'(type, x, y, z, scale, rotation, flip, filename) '
'VALUES (?, ?, ?, ?, ?, ?, ?, ?) ',
('pixmap', 22.2, 33.3, 0.22, 3.4, 45, -1, 'bee.png'))
io.ex('INSERT INTO sqlar (item_id, data) VALUES (?, ?)',
(1, imgdata3x3))
io.connection.commit()
del(io)
io = SQLiteIO(fname, view.scene, readonly=True)
io.read()
item, selected = view.scene.items_to_add.get()
assert selected is False
assert item.save_id == 1
assert item.pos().x() == 22.2
assert item.pos().y() == 33.3
assert item.zValue() == 0.22
assert item.scale() == 3.4
assert item.rotation() == 45
assert item.flip() == -1
assert item.filename == 'bee.png'
assert item.width == 3
assert item.height == 3
assert view.scene.items_to_add.empty() is True
def test_sqliteio_read_updates_progress(view):
worker = MagicMock(canceled=False)
io = SQLiteIO(':memory:', view.scene, create_new=True,
worker=worker)
io.create_schema_on_new()
io.ex('INSERT INTO items (type, x, y, z, scale, filename) '
'VALUES (?, ?, ?, ?, ?, ?) ',
('pixmap', 0, 0, 0, 1, 'bee.png'))
io.ex('INSERT INTO sqlar (item_id, data) VALUES (?, ?)', (1, b''))
io.connection.commit()
io.read()
worker.begin_processing.emit.assert_called_once_with(1)
worker.progress.emit.assert_called_once_with(0)
worker.finished.emit.assert_called_once_with(':memory:', [])
def test_sqliteio_read_canceled(view):
worker = MagicMock(canceled=True)
io = SQLiteIO(':memory:', view.scene, create_new=True, worker=worker)
io.create_schema_on_new()
io.ex('INSERT INTO items (type, x, y, z, scale, filename) '
'VALUES (?, ?, ?, ?, ?, ?) ',
('pixmap', 0, 0, 0, 1, 'bee.png'))
io.ex('INSERT INTO sqlar (item_id, data) VALUES (?, ?)', (1, b''))
io.ex('INSERT INTO items (type, x, y, z, scale, filename) '
'VALUES (?, ?, ?, ?, ?, ?) ',
('pixmap', 50, 50, 0, 1, 'bee2.png'))
io.ex('INSERT INTO sqlar (item_id, data) VALUES (?, ?)', (1, b''))
io.connection.commit()
io.read()
worker.begin_processing.emit.assert_called_once_with(2)
worker.progress.emit.assert_called_once_with(0)
worker.finished.emit.assert_called_once_with('', [])
def test_sqliteio_read_raises_error_when_file_borked(view, tmpdir):
fname = os.path.join(tmpdir, 'test.bee')
with open(fname, 'w') as f:
f.write('foobar')
io = SQLiteIO(fname, view.scene, readonly=True)
with pytest.raises(BeeFileIOError) as exinfo:
io.read()
worker.begin_processing.emit.assert_called_once_with(1)
worker.progress.emit.assert_called_once_with(0)
worker.finished.emit.assert_called_once_with(':memory:', [])
assert exinfo.value.filename == fname
def test_canceled(self):
worker = MagicMock(canceled=True)
io = SQLiteIO(':memory:', self.scene, create_new=True,
worker=worker)
io.create_schema_on_new()
io.ex('INSERT INTO items (type, x, y, z, scale, filename) '
'VALUES (?, ?, ?, ?, ?, ?) ',
('pixmap', 0, 0, 0, 1, 'bee.png'))
io.ex('INSERT INTO sqlar (item_id, data) VALUES (?, ?)', (1, b''))
io.ex('INSERT INTO items (type, x, y, z, scale, filename) '
'VALUES (?, ?, ?, ?, ?, ?) ',
('pixmap', 50, 50, 0, 1, 'bee2.png'))
io.ex('INSERT INTO sqlar (item_id, data) VALUES (?, ?)', (1, b''))
io.connection.commit()
def test_sqliteio_read_emits_error_message_when_file_borked(view, tmpdir):
fname = os.path.join(tmpdir, 'test.bee')
with open(fname, 'w') as f:
f.write('foobar')
worker = MagicMock()
io = SQLiteIO(fname, view.scene, readonly=True, worker=worker)
io.read()
worker.finished.emit.assert_called_once()
args = worker.finished.emit.call_args_list[0][0]
assert args[0] == fname
assert len(args[1]) == 1
def test_sqliteio_read_raises_error_when_file_empty(view, tmpdir):
fname = os.path.join(tmpdir, 'test.bee')
io = SQLiteIO(fname, view.scene, readonly=True)
with pytest.raises(BeeFileIOError) as exinfo:
io.read()
worker.begin_processing.emit.assert_called_once_with(2)
worker.progress.emit.assert_called_once_with(0)
worker.finished.emit.assert_called_once_with('', [])
assert exinfo.value.filename == fname
def test_raises_error_when_file_borked(self):
with tempfile.TemporaryDirectory() as dirname:
fname = os.path.join(dirname, 'test.bee')
with open(fname, 'w') as f:
f.write('foobar')
io = SQLiteIO(fname, self.scene, readonly=True)
with pytest.raises(BeeFileIOError) as exinfo:
io.read()
assert exinfo.value.filename == fname
def test_emits_error_message_when_file_borked(self):
with tempfile.TemporaryDirectory() as dirname:
fname = os.path.join(dirname, 'test.bee')
with open(fname, 'w') as f:
f.write('foobar')
worker = MagicMock()
io = SQLiteIO(fname, self.scene, readonly=True, worker=worker)
io.read()
worker.finished.emit.assert_called_once()
args = worker.finished.emit.call_args_list[0][0]
assert args[0] == ''
assert len(args[1]) == 1
def test_reads_raises_error_when_file_empty(self):
with tempfile.TemporaryDirectory() as dirname:
fname = os.path.join(dirname, 'test.bee')
io = SQLiteIO(fname, self.scene, readonly=True)
with pytest.raises(BeeFileIOError) as exinfo:
io.read()
assert exinfo.value.filename == fname
# should not create a file on reading!
assert os.path.isfile(fname) is False
# should not create a file on reading!
assert os.path.isfile(fname) is False

View file

@ -0,0 +1,128 @@
from unittest.mock import patch, MagicMock, PropertyMock
from PyQt6 import QtCore, QtGui
from beeref.items import BeePixmapItem
def test_set_scale(qapp, imgfilename3x3):
item = BeePixmapItem(QtGui.QImage(imgfilename3x3), imgfilename3x3)
item.prepareGeometryChange = MagicMock()
item.setScale(3)
assert item.scale() == 3
assert item.pos().x() == 0
assert item.pos().y() == 0
item.prepareGeometryChange.assert_called_once()
def test_set_scale_ignores_zero(qapp, item):
item.setScale(0)
assert item.scale() == 1
def test_set_scale_ignores_negative(qapp, item):
item.setScale(-0.1)
assert item.scale() == 1
def test_set_scale_with_anchor(qapp, item):
item.setScale(2, anchor=QtCore.QPointF(100, 100))
assert item.scale() == 2
assert item.pos() == QtCore.QPointF(-100, -100)
def test_set_zvalue_sets_new_max(view, item):
view.scene.addItem(item)
item.setZValue(1.1)
assert item.zValue() == 1.1
assert view.scene.max_z == 1.1
def test_set_zvalue_keeps_old_max(view, item):
view.scene.addItem(item)
view.scene.max_z = 3.3
item.setZValue(1.1)
assert item.zValue() == 1.1
assert view.scene.max_z == 3.3
def test_bring_to_front(view, item):
view.scene.addItem(item)
item.setZValue(3.3)
item2 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item2)
item2.bring_to_front()
assert item2.zValue() > item.zValue()
assert item2.zValue() == view.scene.max_z
def test_set_rotation_no_anchor(qapp, item):
item.setRotation(45)
assert item.rotation() == 45
assert item.pos().x() == 0
assert item.pos().y() == 0
def test_set_rotation_anchor_bottomright_cw(qapp, item):
item.setRotation(90, QtCore.QPointF(100, 100))
assert item.rotation() == 90
assert item.pos().x() == 200
assert item.pos().y() == 0
def test_set_rotation_anchor_bottomright_ccw(qapp, item):
item.setRotation(-90, QtCore.QPointF(100, 100))
assert item.rotation() == 270
assert item.pos().x() == 0
assert item.pos().y() == 200
def test_set_rotation_caps_above_360(qapp, item):
item.setRotation(400)
assert item.rotation() == 40
def test_flip(qapp, item):
assert item.flip() == 1
def test_flip_when_flipped(qapp, item):
item.do_flip()
assert item.flip() == -1
def test_do_flip_no_anchor(qapp, item):
item.do_flip()
assert item.flip() == -1
item.do_flip()
assert item.flip() == 1
def test_do_flip_vertical(qapp, item):
item.do_flip(vertical=True)
assert item.flip() == -1
assert item.rotation() == 180
def test_do_flip_anchor(qapp, item):
item.do_flip(anchor=QtCore.QPointF(100, 100))
assert item.flip() == -1
assert item.pos() == QtCore.QPointF(200, 0)
def test_do_flip_vertical_anchor(qapp, item):
item.do_flip(vertical=True, anchor=QtCore.QPointF(100, 100))
assert item.flip() == -1
assert item.rotation() == 180
assert item.pos() == QtCore.QPointF(0, 200)
def test_base_item_mixin_center_scene_coords(view, item):
view.scene.addItem(item)
item.setPos(5, 5)
item.setScale(2)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
assert item.center_scene_coords == QtCore.QPointF(105, 85)

View file

@ -0,0 +1,99 @@
from unittest.mock import patch, MagicMock
from PyQt6 import QtCore, QtGui
from PyQt6.QtCore import Qt
from beeref.items import BeePixmapItem
from beeref.selection import MultiSelectItem
@patch('beeref.selection.SelectableMixin.init_selectable')
def test_init(selectable_mock):
item = MultiSelectItem()
assert hasattr(item, 'save_id') is False
selectable_mock.assert_called_once()
def test_width():
item = MultiSelectItem()
item.setRect(0, 0, 50, 100)
assert item.width == 50
def test_height():
item = MultiSelectItem()
item.setRect(0, 0, 50, 100)
assert item.height == 100
def test_paint():
item = MultiSelectItem()
item.paint_selectable = MagicMock()
painter = MagicMock()
item.paint(painter, None, None)
item.paint_selectable.assert_called_once()
painter.drawRect.assert_not_called()
def test_has_selection_outline():
item = MultiSelectItem()
item.has_selection_outline() is True
def test_has_selection_handles():
item = MultiSelectItem()
item.has_selection_handles() is True
def test_selection_action_items(view):
item1 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item1)
item1.setSelected(True)
item2 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item2)
item2.setSelected(True)
item3 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item3)
item3.setSelected(False)
action_items = set(view.scene.multi_select_item.selection_action_items())
assert action_items == {item1, item2, view.scene.multi_select_item}
def test_fit_selection_area():
item = MultiSelectItem()
item.setScale(5)
item.setRotation(-20)
item.do_flip()
item.fit_selection_area(QtCore.QRectF(-10, -20, 100, 80))
assert item.pos().x() == -10
assert item.pos().y() == -20
assert item.width == 100
assert item.height == 80
assert item.scale() == 1
assert item.rotation() == 0
assert item.flip() == 1
assert item.isSelected() is True
@patch('PyQt6.QtWidgets.QGraphicsRectItem.mousePressEvent')
def test_mouse_press_event_when_ctrl_leftclick(mouse_mock):
item = MultiSelectItem()
item.fit_selection_area(QtCore.QRectF(0, 0, 100, 80))
event = MagicMock(
button=MagicMock(return_value=Qt.MouseButton.LeftButton),
modifiers=MagicMock(
return_value=Qt.KeyboardModifier.ControlModifier))
item.mousePressEvent(event)
event.ignore.assert_called_once()
mouse_mock.assert_not_called()
@patch('beeref.selection.SelectableMixin.mousePressEvent')
def test_mouse_press_event_when_leftclick(mouse_mock):
item = MultiSelectItem()
item.fit_selection_area(QtCore.QRectF(0, 0, 100, 80))
event = MagicMock(
button=MagicMock(return_value=Qt.MouseButton.LeftButton))
item.mousePressEvent(event)
event.ignore.assert_not_called()
mouse_mock.assert_called_once_with(event)

View file

@ -0,0 +1,33 @@
from PyQt6 import QtCore
from beeref.selection import RubberbandItem
def test_width():
item = RubberbandItem()
item.setRect(5, 5, 100, 80)
assert item.width == 100
def test_height():
item = RubberbandItem()
item.setRect(5, 5, 100, 80)
assert item.height == 80
def test_fit_topleft_to_bottomright():
item = RubberbandItem()
item.fit(QtCore.QPointF(-10, -20), QtCore.QPointF(30, 40))
assert item.rect().topLeft().x() == -10
assert item.rect().topLeft().y() == -20
assert item.rect().bottomRight().x() == 30
assert item.rect().bottomRight().y() == 40
def test_fit_topright_to_bottomleft():
item = RubberbandItem()
item.fit(QtCore.QPointF(50, -20), QtCore.QPointF(-30, 40))
assert item.rect().topLeft().x() == -30
assert item.rect().topLeft().y() == -20
assert item.rect().bottomRight().x() == 50
assert item.rect().bottomRight().y() == 40

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,12 @@
from PyQt6 import QtGui
from beeref.assets import BeeAssets
from .base import BeeTestCase
class GetRectFromPointsTestCase(BeeTestCase):
def test_singleton(view):
assert BeeAssets() is BeeAssets()
assert BeeAssets().logo is BeeAssets().logo
def test_singleton(self):
assert BeeAssets() is BeeAssets()
assert BeeAssets().logo is BeeAssets().logo
def test_has_logo(self):
assert isinstance(BeeAssets().logo, QtGui.QIcon)
def test_has_logo(view):
assert isinstance(BeeAssets().logo, QtGui.QIcon)

View file

@ -4,453 +4,417 @@ from PyQt6 import QtCore, QtGui
from beeref import commands
from beeref.items import BeePixmapItem
from beeref.scene import BeeGraphicsScene
from .base import BeeTestCase
class InsertItemsTestCase(BeeTestCase):
@patch('beeref.scene.BeeGraphicsScene.views')
def test_redo_undo(self, views_mock):
scene = BeeGraphicsScene(None)
view = MagicMock(get_scale=MagicMock(return_value=1))
views_mock.return_value = [view]
scene.update_selection = MagicMock()
scene.max_z = 5
item1 = BeePixmapItem(QtGui.QImage())
scene.addItem(item1)
item2 = BeePixmapItem(QtGui.QImage())
item2.setPos(50, 40)
command = commands.InsertItems(scene, [item2])
command.redo()
assert list(scene.items_for_save()) == [item1, item2]
assert item1.isSelected() is False
assert item2.isSelected() is True
assert item2.pos() == QtCore.QPointF(50, 40)
item2.zValue() > 5
command.undo()
assert list(scene.items_for_save()) == [item1]
assert item1.isSelected() is False
assert item2.pos() == QtCore.QPointF(50, 40)
@patch('beeref.scene.BeeGraphicsScene.views')
def test_redo_undo_with_position(self, views_mock):
scene = BeeGraphicsScene(None)
view = MagicMock(get_scale=MagicMock(return_value=1))
views_mock.return_value = [view]
scene.update_selection = MagicMock()
item1 = BeePixmapItem(QtGui.QImage())
item1.setPos(10, 20)
scene.addItem(item1)
item2 = BeePixmapItem(QtGui.QImage())
item2.setPos(50, 40)
scene.addItem(item2)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.InsertItems(
scene, [item1, item2], QtCore.QPointF(100, 200))
command.redo()
assert set(scene.items_for_save()) == {item1, item2}
assert item1.pos() == QtCore.QPointF(30, 150)
assert item2.pos() == QtCore.QPointF(70, 170)
command.undo()
assert list(scene.items_for_save()) == []
assert item1.pos() == QtCore.QPointF(10, 20)
assert item2.pos() == QtCore.QPointF(50, 40)
@patch('beeref.scene.BeeGraphicsScene.views')
def test_ignore_first_redo(self, views_mock):
scene = BeeGraphicsScene(None)
view = MagicMock(get_scale=MagicMock(return_value=1))
views_mock.return_value = [view]
scene.update_selection = MagicMock()
scene.max_z = 5
item1 = BeePixmapItem(QtGui.QImage())
scene.addItem(item1)
item2 = BeePixmapItem(QtGui.QImage())
command = commands.InsertItems(scene, [item2], ignore_first_redo=True)
command.redo()
assert list(scene.items_for_save()) == [item1]
assert item1.isSelected() is False
command.redo()
assert list(scene.items_for_save()) == [item1, item2]
assert item1.isSelected() is False
assert item2.isSelected() is True
item2.zValue() > 5
def test_insert_items(view):
view.scene.update_selection = MagicMock()
view.scene.max_z = 5
item1 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item1)
item2 = BeePixmapItem(QtGui.QImage())
item2.setPos(50, 40)
command = commands.InsertItems(view.scene, [item2])
command.redo()
assert list(view.scene.items_for_save()) == [item1, item2]
assert item1.isSelected() is False
assert item2.isSelected() is True
assert item2.pos() == QtCore.QPointF(50, 40)
item2.zValue() > 5
command.undo()
assert list(view.scene.items_for_save()) == [item1]
assert item1.isSelected() is False
assert item2.pos() == QtCore.QPointF(50, 40)
class DeleteItemsTestCase(BeeTestCase):
def test_insert_items_with_position(view):
view.scene.update_selection = MagicMock()
def test_redo_undo(self):
scene = BeeGraphicsScene(None)
scene.update_selection = MagicMock()
item1 = BeePixmapItem(QtGui.QImage())
scene.addItem(item1)
item2 = BeePixmapItem(QtGui.QImage())
scene.addItem(item2)
item2.setSelected(True)
command = commands.DeleteItems(scene, [item2])
command.redo()
assert list(scene.items_for_save()) == [item1]
command.undo()
assert list(scene.items_for_save()) == [item1, item2]
assert item1.isSelected() is False
assert item2.isSelected() is True
item1 = BeePixmapItem(QtGui.QImage())
item1.setPos(10, 20)
view.scene.addItem(item1)
item2 = BeePixmapItem(QtGui.QImage())
item2.setPos(50, 40)
view.scene.addItem(item2)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.InsertItems(
view.scene, [item1, item2], QtCore.QPointF(100, 200))
command.redo()
assert set(view.scene.items_for_save()) == {item1, item2}
assert item1.pos() == QtCore.QPointF(30, 150)
assert item2.pos() == QtCore.QPointF(70, 170)
command.undo()
assert list(view.scene.items_for_save()) == []
assert item1.pos() == QtCore.QPointF(10, 20)
assert item2.pos() == QtCore.QPointF(50, 40)
class MoveItemsByTestCase(BeeTestCase):
def test_redo_undo(self):
item1 = BeePixmapItem(QtGui.QImage())
item1.setPos(0, 0)
item2 = BeePixmapItem(QtGui.QImage())
item2.setPos(30, 40)
command = commands.MoveItemsBy([item1, item2], QtCore.QPointF(50, 100))
command.redo()
assert item1.pos().x() == 50
assert item1.pos().y() == 100
assert item2.pos().x() == 80
assert item2.pos().y() == 140
command.undo()
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.pos().x() == 30
assert item2.pos().y() == 40
def test_ignore_first_redo(self):
item1 = BeePixmapItem(QtGui.QImage())
item1.setPos(0, 0)
item2 = BeePixmapItem(QtGui.QImage())
item2.setPos(30, 40)
command = commands.MoveItemsBy([item1, item2],
QtCore.QPointF(50, 100),
ignore_first_redo=True)
command.redo()
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.pos().x() == 30
assert item2.pos().y() == 40
command.redo()
assert item1.pos().x() == 50
assert item1.pos().y() == 100
assert item2.pos().x() == 80
assert item2.pos().y() == 140
def test_insert_items_ignore_first_redo(view):
view.scene.update_selection = MagicMock()
view.scene.max_z = 5
item1 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item1)
item2 = BeePixmapItem(QtGui.QImage())
command = commands.InsertItems(view.scene, [item2], ignore_first_redo=True)
command.redo()
assert list(view.scene.items_for_save()) == [item1]
assert item1.isSelected() is False
command.redo()
assert list(view.scene.items_for_save()) == [item1, item2]
assert item1.isSelected() is False
assert item2.isSelected() is True
item2.zValue() > 5
class ScaleItemsByTestCase(BeeTestCase):
def test_redo_undo(self):
item1 = BeePixmapItem(QtGui.QImage())
item1.setScale(1)
item2 = BeePixmapItem(QtGui.QImage())
item2.setScale(3)
item2.setPos(100, 100)
command = commands.ScaleItemsBy([item1, item2], 2,
QtCore.QPointF(100, 100))
command.redo()
assert item1.scale() == 2
assert item1.pos().x() == -100
assert item1.pos().y() == -100
assert item2.scale() == 6
assert item2.pos().x() == 100
assert item2.pos().y() == 100
command.undo()
assert item1.scale() == 1
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.scale() == 3
assert item2.pos().x() == 100
assert item2.pos().y() == 100
def test_ignore_first_redo(self):
item1 = BeePixmapItem(QtGui.QImage())
item1.setScale(1)
item2 = BeePixmapItem(QtGui.QImage())
item2.setScale(3)
item2.setPos(100, 100)
command = commands.ScaleItemsBy([item1, item2], 2,
QtCore.QPointF(100, 100),
ignore_first_redo=True)
command.redo()
assert item1.scale() == 1
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.scale() == 3
assert item2.pos().x() == 100
assert item2.pos().y() == 100
command.redo()
assert item1.scale() == 2
assert item1.pos().x() == -100
assert item1.pos().y() == -100
assert item2.scale() == 6
assert item2.pos().x() == 100
assert item2.pos().y() == 100
def test_delete_items(view):
view.scene.update_selection = MagicMock()
item1 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item1)
item2 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item2)
item2.setSelected(True)
command = commands.DeleteItems(view.scene, [item2])
command.redo()
assert list(view.scene.items_for_save()) == [item1]
command.undo()
assert list(view.scene.items_for_save()) == [item1, item2]
assert item1.isSelected() is False
assert item2.isSelected() is True
class RotateItemsByTestCase(BeeTestCase):
def test_move_items_by(qapp):
item1 = BeePixmapItem(QtGui.QImage())
item1.setPos(0, 0)
item2 = BeePixmapItem(QtGui.QImage())
item2.setPos(30, 40)
command = commands.MoveItemsBy([item1, item2], QtCore.QPointF(50, 100))
command.redo()
assert item1.pos().x() == 50
assert item1.pos().y() == 100
assert item2.pos().x() == 80
assert item2.pos().y() == 140
def test_redo_undo(self):
item1 = BeePixmapItem(QtGui.QImage())
item1.setRotation(0)
item2 = BeePixmapItem(QtGui.QImage())
item2.setRotation(30)
item2.setPos(100, 100)
item2.do_flip()
command = commands.RotateItemsBy([item1, item2], -90,
QtCore.QPointF(100, 100))
command.redo()
assert item1.rotation() == 270
assert item1.pos().x() == 0
assert item1.pos().y() == 200
assert item2.rotation() == 120
assert item2.pos().x() == 100
assert item2.pos().y() == 100
command.undo()
assert item1.rotation() == 0
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.rotation() == 30
assert item2.pos().x() == 100
assert item2.pos().y() == 100
def test_ignore_first_redo(self):
item1 = BeePixmapItem(QtGui.QImage())
item1.setRotation(0)
item2 = BeePixmapItem(QtGui.QImage())
item2.setRotation(30)
item2.setPos(100, 100)
item2.do_flip()
command = commands.RotateItemsBy([item1, item2], -90,
QtCore.QPointF(100, 100),
ignore_first_redo=True)
command.redo()
assert item1.rotation() == 0
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.rotation() == 30
assert item2.pos().x() == 100
assert item2.pos().y() == 100
command.redo()
assert item1.rotation() == 270
assert item1.pos().x() == 0
assert item1.pos().y() == 200
assert item2.rotation() == 120
assert item2.pos().x() == 100
assert item2.pos().y() == 100
command.undo()
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.pos().x() == 30
assert item2.pos().y() == 40
class NormalizeItemsTestCase(BeeTestCase):
def test_redo_undo(self):
item1 = BeePixmapItem(QtGui.QImage())
item1.setScale(1)
item2 = BeePixmapItem(QtGui.QImage())
item2.setScale(3)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.NormalizeItems([item1, item2], [2, 0.5])
command.redo()
assert item1.scale() == 2
assert item1.pos() == QtCore.QPointF(-50, -40)
assert item2.scale() == 1.5
assert item2.pos() == QtCore.QPointF(75, 60)
command.undo()
assert item1.scale() == 1
assert item1.pos() == QtCore.QPointF(0, 0)
assert item2.scale() == 3
assert item2.pos() == QtCore.QPointF(0, 0)
def test_move_items_by_ignore_first_redo(qapp):
item1 = BeePixmapItem(QtGui.QImage())
item1.setPos(0, 0)
item2 = BeePixmapItem(QtGui.QImage())
item2.setPos(30, 40)
command = commands.MoveItemsBy([item1, item2],
QtCore.QPointF(50, 100),
ignore_first_redo=True)
command.redo()
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.pos().x() == 30
assert item2.pos().y() == 40
command.redo()
assert item1.pos().x() == 50
assert item1.pos().y() == 100
assert item2.pos().x() == 80
assert item2.pos().y() == 140
class FlipItemsTestCase(BeeTestCase):
def test_scale_items_by(view):
item1 = BeePixmapItem(QtGui.QImage())
item1.setScale(1)
item2 = BeePixmapItem(QtGui.QImage())
item2.setScale(3)
item2.setPos(100, 100)
command = commands.ScaleItemsBy([item1, item2], 2,
QtCore.QPointF(100, 100))
command.redo()
assert item1.scale() == 2
assert item1.pos().x() == -100
assert item1.pos().y() == -100
assert item2.scale() == 6
assert item2.pos().x() == 100
assert item2.pos().y() == 100
command.undo()
assert item1.scale() == 1
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.scale() == 3
assert item2.pos().x() == 100
assert item2.pos().y() == 100
def test_redo_undo_horizontal(self):
item1 = BeePixmapItem(QtGui.QImage())
item1.setRotation(0)
item2 = BeePixmapItem(QtGui.QImage())
item2.setRotation(30)
item2.setPos(100, 100)
item2.do_flip()
command = commands.FlipItems([item1, item2],
def test_scale_items_by_ignore_first_redo(qapp):
item1 = BeePixmapItem(QtGui.QImage())
item1.setScale(1)
item2 = BeePixmapItem(QtGui.QImage())
item2.setScale(3)
item2.setPos(100, 100)
command = commands.ScaleItemsBy([item1, item2], 2,
QtCore.QPointF(100, 100),
ignore_first_redo=True)
command.redo()
assert item1.scale() == 1
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.scale() == 3
assert item2.pos().x() == 100
assert item2.pos().y() == 100
command.redo()
assert item1.scale() == 2
assert item1.pos().x() == -100
assert item1.pos().y() == -100
assert item2.scale() == 6
assert item2.pos().x() == 100
assert item2.pos().y() == 100
def test_rotate_items_by(qapp):
item1 = BeePixmapItem(QtGui.QImage())
item1.setRotation(0)
item2 = BeePixmapItem(QtGui.QImage())
item2.setRotation(30)
item2.setPos(100, 100)
item2.do_flip()
command = commands.RotateItemsBy([item1, item2], -90,
QtCore.QPointF(100, 100))
command.redo()
assert item1.rotation() == 270
assert item1.pos().x() == 0
assert item1.pos().y() == 200
assert item2.rotation() == 120
assert item2.pos().x() == 100
assert item2.pos().y() == 100
command.undo()
assert item1.rotation() == 0
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.rotation() == 30
assert item2.pos().x() == 100
assert item2.pos().y() == 100
def test_rotate_items_by_ignore_first_redo(qapp):
item1 = BeePixmapItem(QtGui.QImage())
item1.setRotation(0)
item2 = BeePixmapItem(QtGui.QImage())
item2.setRotation(30)
item2.setPos(100, 100)
item2.do_flip()
command = commands.RotateItemsBy([item1, item2], -90,
QtCore.QPointF(100, 100),
vertical=False)
command.redo()
assert item1.flip() == -1
assert item1.rotation() == 0
assert item1.pos() == QtCore.QPointF(200, 0)
assert item2.flip() == 1
assert item2.rotation() == 30
assert item2.pos() == QtCore.QPointF(100, 100)
command.undo()
assert item1.flip() == 1
assert item1.rotation() == 0
assert item1.pos() == QtCore.QPointF(0, 0)
assert item2.flip() == -1
assert item2.rotation() == 30
assert item2.pos() == QtCore.QPointF(100, 100)
def test_redo_undo_vertical(self):
item1 = BeePixmapItem(QtGui.QImage())
item1.setRotation(0)
item2 = BeePixmapItem(QtGui.QImage())
item2.setRotation(30)
item2.setPos(100, 100)
item2.do_flip()
command = commands.FlipItems([item1, item2],
QtCore.QPointF(100, 100),
vertical=True)
command.redo()
assert item1.flip() == -1
assert item1.rotation() == 180
assert item1.pos() == QtCore.QPointF(0, 200)
assert item2.flip() == 1
assert item2.rotation() == 210
assert item2.pos() == QtCore.QPointF(100, 100)
command.undo()
assert item1.flip() == 1
assert item1.rotation() == 0
assert item1.pos() == QtCore.QPointF(0, 0)
assert item2.flip() == -1
assert item2.rotation() == 30
assert item2.pos() == QtCore.QPointF(100, 100)
ignore_first_redo=True)
command.redo()
assert item1.rotation() == 0
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.rotation() == 30
assert item2.pos().x() == 100
assert item2.pos().y() == 100
command.redo()
assert item1.rotation() == 270
assert item1.pos().x() == 0
assert item1.pos().y() == 200
assert item2.rotation() == 120
assert item2.pos().x() == 100
assert item2.pos().y() == 100
class ResetScaleTestCase(BeeTestCase):
def test_redo_undo(self):
item = BeePixmapItem(QtGui.QImage())
item.setScale(2)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.ResetScale([item])
command.redo()
assert item.scale() == 1
assert item.pos().x() == 50
assert item.pos().y() == 40
command.undo()
assert item.scale() == 2
assert item.pos().x() == 0
assert item.pos().y() == 0
def test_normalize_items(qapp):
item1 = BeePixmapItem(QtGui.QImage())
item1.setScale(1)
item2 = BeePixmapItem(QtGui.QImage())
item2.setScale(3)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.NormalizeItems([item1, item2], [2, 0.5])
command.redo()
assert item1.scale() == 2
assert item1.pos() == QtCore.QPointF(-50, -40)
assert item2.scale() == 1.5
assert item2.pos() == QtCore.QPointF(75, 60)
command.undo()
assert item1.scale() == 1
assert item1.pos() == QtCore.QPointF(0, 0)
assert item2.scale() == 3
assert item2.pos() == QtCore.QPointF(0, 0)
class ResetRotateTestCase(BeeTestCase):
def test_flip_items_horizontal(qapp):
item1 = BeePixmapItem(QtGui.QImage())
item1.setRotation(0)
def test_redo_undo(self):
item = BeePixmapItem(QtGui.QImage())
item.setRotation(180)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.ResetRotation([item])
command.redo()
assert item.rotation() == 0
assert item.pos().x() == -100
assert item.pos().y() == -80
command.undo()
assert item.rotation() == 180
assert item.pos().x() == 0
assert item.pos().y() == 0
item2 = BeePixmapItem(QtGui.QImage())
item2.setRotation(30)
item2.setPos(100, 100)
item2.do_flip()
command = commands.FlipItems([item1, item2],
QtCore.QPointF(100, 100),
vertical=False)
command.redo()
assert item1.flip() == -1
assert item1.rotation() == 0
assert item1.pos() == QtCore.QPointF(200, 0)
assert item2.flip() == 1
assert item2.rotation() == 30
assert item2.pos() == QtCore.QPointF(100, 100)
command.undo()
assert item1.flip() == 1
assert item1.rotation() == 0
assert item1.pos() == QtCore.QPointF(0, 0)
assert item2.flip() == -1
assert item2.rotation() == 30
assert item2.pos() == QtCore.QPointF(100, 100)
class ResetFlipTestCase(BeeTestCase):
def test_flip_items_vertical(qapp):
item1 = BeePixmapItem(QtGui.QImage())
item1.setRotation(0)
def test_redo_undo(self):
item1 = BeePixmapItem(QtGui.QImage())
item1.do_flip()
item2 = BeePixmapItem(QtGui.QImage())
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.ResetFlip([item1, item2])
command.redo()
assert item1.flip() == 1
assert item1.pos().x() == -100
assert item1.pos().y() == 0
assert item2.flip() == 1
assert item2.pos().x() == 0
assert item2.pos().y() == 0
command.undo()
assert item1.flip() == -1
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.flip() == 1
assert item2.pos().x() == 0
assert item2.pos().y() == 0
item2 = BeePixmapItem(QtGui.QImage())
item2.setRotation(30)
item2.setPos(100, 100)
item2.do_flip()
command = commands.FlipItems([item1, item2],
QtCore.QPointF(100, 100),
vertical=True)
command.redo()
assert item1.flip() == -1
assert item1.rotation() == 180
assert item1.pos() == QtCore.QPointF(0, 200)
assert item2.flip() == 1
assert item2.rotation() == 210
assert item2.pos() == QtCore.QPointF(100, 100)
command.undo()
assert item1.flip() == 1
assert item1.rotation() == 0
assert item1.pos() == QtCore.QPointF(0, 0)
assert item2.flip() == -1
assert item2.rotation() == 30
assert item2.pos() == QtCore.QPointF(100, 100)
class ResetTransformsTestCase(BeeTestCase):
def test_redo_undo(self):
item1 = BeePixmapItem(QtGui.QImage())
item1.setScale(2)
item1.do_flip()
item2 = BeePixmapItem(QtGui.QImage())
item2.setRotation(180)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.ResetTransforms([item1, item2])
command.redo()
assert item1.scale() == 1
assert item1.rotation() == 0
assert item1.flip() == 1
assert item1.pos().x() == -150
assert item1.pos().y() == 40
assert item2.scale() == 1
assert item2.rotation() == 0
assert item2.flip() == 1
assert item2.pos().x() == -100
assert item2.pos().y() == -80
command.undo()
assert item1.scale() == 2
assert item1.rotation() == 0
assert item1.flip() == -1
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.scale() == 1
assert item2.rotation() == 180
assert item2.flip() == 1
assert item2.pos().x() == 0
assert item2.pos().y() == 0
def test_reset_scale(view, item):
item.setScale(2)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.ResetScale([item])
command.redo()
assert item.scale() == 1
assert item.pos().x() == 50
assert item.pos().y() == 40
command.undo()
assert item.scale() == 2
assert item.pos().x() == 0
assert item.pos().y() == 0
class ArrangeItemsTestCase(BeeTestCase):
def test_reset_rotate(view, item):
item.setRotation(180)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.ResetRotation([item])
command.redo()
assert item.rotation() == 0
assert item.pos().x() == -100
assert item.pos().y() == -80
command.undo()
assert item.rotation() == 180
assert item.pos().x() == 0
assert item.pos().y() == 0
def test_redo_undo(self):
scene = BeeGraphicsScene(None)
item1 = BeePixmapItem(QtGui.QImage())
item1.do_flip()
scene.addItem(item1)
item2 = BeePixmapItem(QtGui.QImage())
item2.setRotation(90)
scene.addItem(item2)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.ArrangeItems(
scene,
[item1, item2],
[QtCore.QPointF(1, 2), QtCore.QPointF(203, 204)])
command.redo()
assert item1.pos() == QtCore.QPointF(101, 2)
assert item2.pos() == QtCore.QPointF(283, 204)
command.undo()
assert item1.pos() == QtCore.QPointF(0, 0)
assert item2.pos() == QtCore.QPointF(0, 0)
def test_reset_flip(qapp):
item1 = BeePixmapItem(QtGui.QImage())
item1.do_flip()
item2 = BeePixmapItem(QtGui.QImage())
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.ResetFlip([item1, item2])
command.redo()
assert item1.flip() == 1
assert item1.pos().x() == -100
assert item1.pos().y() == 0
assert item2.flip() == 1
assert item2.pos().x() == 0
assert item2.pos().y() == 0
command.undo()
assert item1.flip() == -1
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.flip() == 1
assert item2.pos().x() == 0
assert item2.pos().y() == 0
def test_reset_transforms(qapp):
item1 = BeePixmapItem(QtGui.QImage())
item1.setScale(2)
item1.do_flip()
item2 = BeePixmapItem(QtGui.QImage())
item2.setRotation(180)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.ResetTransforms([item1, item2])
command.redo()
assert item1.scale() == 1
assert item1.rotation() == 0
assert item1.flip() == 1
assert item1.pos().x() == -150
assert item1.pos().y() == 40
assert item2.scale() == 1
assert item2.rotation() == 0
assert item2.flip() == 1
assert item2.pos().x() == -100
assert item2.pos().y() == -80
command.undo()
assert item1.scale() == 2
assert item1.rotation() == 0
assert item1.flip() == -1
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.scale() == 1
assert item2.rotation() == 180
assert item2.flip() == 1
assert item2.pos().x() == 0
assert item2.pos().y() == 0
def test_arrange_items(view):
item1 = BeePixmapItem(QtGui.QImage())
item1.do_flip()
view.scene.addItem(item1)
item2 = BeePixmapItem(QtGui.QImage())
item2.setRotation(90)
view.scene.addItem(item2)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.ArrangeItems(
view.scene,
[item1, item2],
[QtCore.QPointF(1, 2), QtCore.QPointF(203, 204)])
command.redo()
assert item1.pos() == QtCore.QPointF(101, 2)
assert item2.pos() == QtCore.QPointF(283, 204)
command.undo()
assert item1.pos() == QtCore.QPointF(0, 0)
assert item2.pos() == QtCore.QPointF(0, 0)

View file

@ -4,67 +4,70 @@ from unittest.mock import patch
import pytest
from beeref.config import CommandlineArgs, BeeSettings
from .base import BeeTestCase
from beeref.config import CommandlineArgs
class CommandlineArgsTestCase(BeeTestCase):
def test_singleton(self):
assert CommandlineArgs() is CommandlineArgs()
assert CommandlineArgs()._args is CommandlineArgs()._args
@patch('beeref.config.parser.parse_args')
def test_with_check_forces_new_parsing(self, parse_mock):
args1 = CommandlineArgs()
args2 = CommandlineArgs(with_check=True)
parse_mock.assert_called_once()
assert args1 is not args2
def test_get(self):
args = CommandlineArgs()
assert args.loglevel == 'INFO'
def test_get_unknown(self):
args = CommandlineArgs()
with pytest.raises(AttributeError):
args.foo
def test_command_line_args_singleton():
assert CommandlineArgs() is CommandlineArgs()
assert CommandlineArgs()._args is CommandlineArgs()._args
CommandlineArgs._instance = None
class BeeSettingsRecentFilesTestCase(BeeTestCase):
@patch('beeref.config.parser.parse_args')
def test_command_line_args_with_check_forces_new_parsing(parse_mock):
args1 = CommandlineArgs()
args2 = CommandlineArgs(with_check=True)
parse_mock.assert_called_once()
assert args1 is not args2
CommandlineArgs._instance = None
def setUp(self):
self.settings = BeeSettings()
def test_get_empty(self):
self.settings.get_recent_files() == []
def test_command_line_args_get():
args = CommandlineArgs()
assert args.loglevel == 'INFO'
CommandlineArgs._instance = None
def test_get_existing_only(self):
with tempfile.NamedTemporaryFile() as f:
self.settings.update_recent_files('foo.bee')
self.settings.update_recent_files(f.name)
self.settings.get_recent_files(existing_only=True) == [f.name]
def test_update(self):
self.settings.update_recent_files('foo.bee')
self.settings.update_recent_files('bar.bee')
assert self.settings.get_recent_files() == [
os.path.abspath('bar.bee'),
os.path.abspath('foo.bee')]
def test_command_line_args_get_unknown():
args = CommandlineArgs()
with pytest.raises(AttributeError):
args.foo
CommandlineArgs._instance = None
def test_update_existing(self):
self.settings.update_recent_files('foo.bee')
self.settings.update_recent_files('bar.bee')
self.settings.update_recent_files('foo.bee')
assert self.settings.get_recent_files() == [
os.path.abspath('foo.bee'),
os.path.abspath('bar.bee')]
def test_update_respects_max_num(self):
for i in range(15):
self.settings.update_recent_files(f'{i}.bee')
def test_settings_recent_files_get_empty(settings):
settings.get_recent_files() == []
recent = self.settings.get_recent_files()
assert len(recent) == 10
assert recent[0] == os.path.abspath('14.bee')
assert recent[-1] == os.path.abspath('5.bee')
def test_settings_recent_files_get_existing_only(settings):
with tempfile.NamedTemporaryFile() as f:
settings.update_recent_files('foo.bee')
settings.update_recent_files(f.name)
settings.get_recent_files(existing_only=True) == [f.name]
def test_settings_recent_files_update(settings):
settings.update_recent_files('foo.bee')
settings.update_recent_files('bar.bee')
assert settings.get_recent_files() == [
os.path.abspath('bar.bee'),
os.path.abspath('foo.bee')]
def test_settings_recent_files_update_existing(settings):
settings.update_recent_files('foo.bee')
settings.update_recent_files('bar.bee')
settings.update_recent_files('foo.bee')
assert settings.get_recent_files() == [
os.path.abspath('foo.bee'),
os.path.abspath('bar.bee')]
def test_settings_recent_files_update_respects_max_num(settings):
for i in range(15):
settings.update_recent_files(f'{i}.bee')
recent = settings.get_recent_files()
assert len(recent) == 10
assert recent[0] == os.path.abspath('14.bee')
assert recent[-1] == os.path.abspath('5.bee')

18
tests/test_gui.py Normal file
View file

@ -0,0 +1,18 @@
from PyQt6 import QtWidgets
from PyQt6.QtCore import Qt
from beeref.config import logfile_name
from beeref.gui import DebugLogDialog
def test_debug_log_dialog(qtbot, settings, view):
with open(logfile_name(), 'w') as f:
f.write('my log output')
dialog = DebugLogDialog(view)
dialog.show()
qtbot.addWidget(dialog)
assert dialog.log.text() == 'my log output'
qtbot.mouseClick(dialog.copy_button, Qt.MouseButton.LeftButton)
clipboard = QtWidgets.QApplication.clipboard()
assert clipboard.text() == 'my log output'

View file

@ -3,138 +3,130 @@ from unittest.mock import patch, MagicMock, PropertyMock
from PyQt6 import QtCore, QtGui
from beeref.items import BeePixmapItem
from beeref.scene import BeeGraphicsScene
from .base import BeeTestCase
class BeePixmapItemTestCase(BeeTestCase):
@patch('beeref.selection.SelectableMixin.init_selectable')
def test_init(selectable_mock, qapp, imgfilename3x3):
item = BeePixmapItem(QtGui.QImage(imgfilename3x3), imgfilename3x3)
assert item.save_id is None
assert item.width == 3
assert item.height == 3
assert item.scale() == 1
assert item.filename == imgfilename3x3
selectable_mock.assert_called_once()
def setUp(self):
self.scene = BeeGraphicsScene(None)
@patch('beeref.selection.SelectableMixin.init_selectable')
def test_init(self, selectable_mock):
item = BeePixmapItem(
QtGui.QImage(self.imgfilename3x3), self.imgfilename3x3)
assert item.save_id is None
assert item.width == 3
assert item.height == 3
assert item.scale() == 1
assert item.filename == self.imgfilename3x3
selectable_mock.assert_called_once()
def test_set_pos_center(qapp, item):
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=200):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=100):
item.set_pos_center(QtCore.QPointF(0, 0))
assert item.pos().x() == -100
assert item.pos().y() == -50
def test_set_pos_center(self):
item = BeePixmapItem(QtGui.QImage())
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=200):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=100):
item.set_pos_center(QtCore.QPointF(0, 0))
assert item.pos().x() == -100
assert item.pos().y() == -50
def test_set_pos_center_when_scaled(self):
item = BeePixmapItem(QtGui.QImage())
item.setScale(2)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=200):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=100):
item.set_pos_center(QtCore.QPointF(0, 0))
assert item.pos().x() == -200
assert item.pos().y() == -100
def test_set_pos_center_when_scaled(qapp, item):
item.setScale(2)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=200):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=100):
item.set_pos_center(QtCore.QPointF(0, 0))
assert item.pos().x() == -200
assert item.pos().y() == -100
def test_set_pos_center_when_rotated(self):
item = BeePixmapItem(QtGui.QImage())
item.setRotation(90)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=200):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=100):
item.set_pos_center(QtCore.QPointF(0, 0))
assert item.pos().x() == 50
assert item.pos().y() == -100
def test_pixmap_to_bytes(self):
item = BeePixmapItem(QtGui.QImage(self.imgfilename3x3))
assert item.pixmap_to_bytes().startswith(b'\x89PNG')
def test_set_pos_center_when_rotated(qapp, item):
item.setRotation(90)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=200):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=100):
item.set_pos_center(QtCore.QPointF(0, 0))
assert item.pos().x() == 50
assert item.pos().y() == -100
def test_pixmap_from_bytes(self):
item = BeePixmapItem(QtGui.QImage())
with open(self.imgfilename3x3, 'rb') as f:
imgdata = f.read()
item.pixmap_from_bytes(imgdata)
assert item.width == 3
assert item.height == 3
def test_paint(self):
item = BeePixmapItem(QtGui.QImage())
item.pixmap = MagicMock(return_value='bee')
item.paint_selectable = MagicMock()
painter = MagicMock()
item.paint(painter, None, None)
item.paint_selectable.assert_called_once()
painter.drawPixmap.assert_called_with(0, 0, 'bee')
def test_pixmap_to_bytes(qapp, imgfilename3x3):
item = BeePixmapItem(QtGui.QImage(imgfilename3x3))
assert item.pixmap_to_bytes().startswith(b'\x89PNG')
def test_has_selection_outline_when_not_selected(self):
item = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item)
item.setSelected(False)
item.has_selection_outline() is False
def test_has_selection_outline_when_selected(self):
item = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item)
item.setSelected(True)
item.has_selection_outline() is True
def test_pixmap_from_bytes(qapp, item, imgfilename3x3):
with open(imgfilename3x3, 'rb') as f:
imgdata = f.read()
item.pixmap_from_bytes(imgdata)
assert item.width == 3
assert item.height == 3
def test_has_selection_handles_when_not_selected(self):
item1 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item1)
item1.setSelected(False)
item2 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item2)
item2.setSelected(False)
item1.has_selection_handles() is False
def test_has_selection_handles_when_selected_single(self):
item1 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item1)
item1.setSelected(True)
item2 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item2)
item2.setSelected(False)
item1.has_selection_handles() is True
def test_paint(qapp, item):
item.pixmap = MagicMock(return_value='bee')
item.paint_selectable = MagicMock()
painter = MagicMock()
item.paint(painter, None, None)
item.paint_selectable.assert_called_once()
painter.drawPixmap.assert_called_with(0, 0, 'bee')
def test_has_selection_handles_when_selected_multi(self):
view = MagicMock(get_scale=MagicMock(return_value=1))
with patch('beeref.scene.BeeGraphicsScene.views',
return_value=[view]):
item1 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item1)
item1.setSelected(True)
item2 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item2)
item2.setSelected(True)
item1.has_selection_handles() is False
def test_selection_action_items(self):
item = BeePixmapItem(QtGui.QImage())
assert item.selection_action_items() == [item]
def test_has_selection_outline_when_not_selected(view, item):
view.scene.addItem(item)
item.setSelected(False)
item.has_selection_outline() is False
def test_create_copy(self):
item = BeePixmapItem(QtGui.QImage(self.imgfilename3x3), 'foo.png')
item.setPos(20, 30)
item.setRotation(33)
item.do_flip()
item.setZValue(0.5)
item.setScale(2.2)
copy = item.create_copy()
assert copy.pixmap_to_bytes() == item.pixmap_to_bytes()
assert copy.filename == 'foo.png'
assert copy.pos() == QtCore.QPointF(20, 30)
assert copy.rotation() == 33
assert item.flip() == -1
assert item.zValue() == 0.5
assert item.scale() == 2.2
def test_has_selection_outline_when_selected(view, item):
view.scene.addItem(item)
item.setSelected(True)
item.has_selection_outline() is True
def test_has_selection_handles_when_not_selected(view, item):
view.scene.addItem(item)
item.setSelected(False)
item2 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item2)
item2.setSelected(False)
item.has_selection_handles() is False
def test_has_selection_handles_when_selected_single(view, item):
view.scene.addItem(item)
item.setSelected(True)
item2 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item2)
item2.setSelected(False)
item.has_selection_handles() is True
def test_has_selection_handles_when_selected_multi(view, item):
view.scene.addItem(item)
item.setSelected(True)
item2 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item2)
item2.setSelected(True)
item.has_selection_handles() is False
def test_selection_action_items(qapp):
item = BeePixmapItem(QtGui.QImage())
assert item.selection_action_items() == [item]
def test_create_copy(qapp, imgfilename3x3):
item = BeePixmapItem(QtGui.QImage(imgfilename3x3), 'foo.png')
item.setPos(20, 30)
item.setRotation(33)
item.do_flip()
item.setZValue(0.5)
item.setScale(2.2)
copy = item.create_copy()
assert copy.pixmap_to_bytes() == item.pixmap_to_bytes()
assert copy.filename == 'foo.png'
assert copy.pos() == QtCore.QPointF(20, 30)
assert copy.rotation() == 33
assert item.flip() == -1
assert item.zValue() == 0.5
assert item.scale() == 2.2

View file

@ -6,31 +6,25 @@ from beeref.__main__ import BeeRefMainWindow, main
from beeref.assets import BeeAssets
from beeref.view import BeeGraphicsView
from .base import BeeTestCase
@patch('PyQt6.QtWidgets.QWidget.show')
def test_beeref_mainwindow_init(show_mock, qapp):
window = BeeRefMainWindow(qapp)
assert window.windowTitle() == 'BeeRef'
assert BeeAssets().logo == BeeAssets().logo
assert window.windowIcon()
assert window.contentsMargins() == QtCore.QMargins(0, 0, 0, 0)
assert isinstance(window.view, BeeGraphicsView)
show_mock.assert_called_once()
class BeeRefMainWindowTestCase(BeeTestCase):
@patch('PyQt6.QtWidgets.QWidget.show')
def test_init(self, show_mock):
window = BeeRefMainWindow(self.app)
assert window.windowTitle() == 'BeeRef'
assert BeeAssets().logo == BeeAssets().logo
assert window.windowIcon()
assert window.contentsMargins() == QtCore.QMargins(0, 0, 0, 0)
assert isinstance(window.view, BeeGraphicsView)
show_mock.assert_called_once()
class BeeRefMainTestCase(BeeTestCase):
@patch('PyQt6.QtWidgets.QApplication')
@patch('beeref.__main__.CommandlineArgs')
def test_run(self, args_mock, app_mock):
app_mock.return_value = self.app
args_mock.return_value.filename = None
args_mock.return_value.loglevel = 'WARN'
with patch.object(self.app, 'exec') as exec_mock:
main()
args_mock.assert_called_once_with(with_check=True)
exec_mock.assert_called_once_with()
@patch('PyQt6.QtWidgets.QApplication')
@patch('beeref.__main__.CommandlineArgs')
def test_main(args_mock, app_mock, qapp):
app_mock.return_value = qapp
args_mock.return_value.filename = None
args_mock.return_value.loglevel = 'WARN'
with patch.object(qapp, 'exec') as exec_mock:
main()
args_mock.assert_called_once_with(with_check=True)
exec_mock.assert_called_once_with()

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,31 +1,28 @@
import logging
import os.path
import pytest
import tempfile
from PyQt6 import QtCore
from beeref import utils
from .base import BeeTestCase
class GetRectFromPointsTestCase(BeeTestCase):
def test_get_rect_from_points_given_topleft_bottomright():
rect = utils.get_rect_from_points(QtCore.QPointF(-10, -20),
QtCore.QPointF(30, 40))
assert rect.topLeft().x() == -10
assert rect.topLeft().y() == -20
assert rect.bottomRight().x() == 30
assert rect.bottomRight().y() == 40
def test_given_topleft_bottomright(self):
rect = utils.get_rect_from_points(QtCore.QPointF(-10, -20),
QtCore.QPointF(30, 40))
assert rect.topLeft().x() == -10
assert rect.topLeft().y() == -20
assert rect.bottomRight().x() == 30
assert rect.bottomRight().y() == 40
def test_given_topright_bottomleft(self):
rect = utils.get_rect_from_points(QtCore.QPointF(50, -20),
QtCore.QPointF(-30, 40))
assert rect.topLeft().x() == -30
assert rect.topLeft().y() == -20
assert rect.bottomRight().x() == 50
assert rect.bottomRight().y() == 40
def test_get_rect_from_points_given_topright_bottomleft():
rect = utils.get_rect_from_points(QtCore.QPointF(50, -20),
QtCore.QPointF(-30, 40))
assert rect.topLeft().x() == -30
assert rect.topLeft().y() == -20
assert rect.bottomRight().x() == 50
assert rect.bottomRight().y() == 40
@pytest.mark.parametrize('number,base,expected',
@ -37,22 +34,19 @@ def test_round_to(number, base, expected):
assert utils.round_to(number, base) == expected
class BeeRotatingFileHandlerTestCase(BeeTestCase):
def test_rotating_file_handler_creates_new_dir(tmpdir):
logfile = os.path.join(tmpdir, 'foo', 'bar.log')
handler = utils.BeeRotatingFileHandler(logfile)
handler.emit(logging.LogRecord(
'foo', logging.INFO, 'bar', 66, 'baz', [], None))
handler.close()
assert os.path.exists(logfile)
def test_creates_new_dir(self):
with tempfile.TemporaryDirectory() as tmpdir:
logfile = os.path.join(tmpdir, 'foo', 'bar.log')
handler = utils.BeeRotatingFileHandler(logfile)
handler.emit(logging.LogRecord(
'foo', logging.INFO, 'bar', 66, 'baz', [], None))
handler.close()
assert os.path.exists(logfile)
def test_uses_existing_dir(self):
with tempfile.TemporaryDirectory() as tmpdir:
logfile = os.path.join(tmpdir, 'bar.log')
handler = utils.BeeRotatingFileHandler(logfile)
handler.emit(logging.LogRecord(
'foo', logging.INFO, 'bar', 66, 'baz', [], None))
handler.close()
assert os.path.exists(logfile)
def testrotating_file_handler_uses_existing_dir(tmpdir):
logfile = os.path.join(tmpdir, 'bar.log')
handler = utils.BeeRotatingFileHandler(logfile)
handler.emit(logging.LogRecord(
'foo', logging.INFO, 'bar', 66, 'baz', [], None))
handler.close()
assert os.path.exists(logfile)

File diff suppressed because it is too large Load diff

5
tests/utils.py Normal file
View file

@ -0,0 +1,5 @@
def queue2list(queue):
qlist = []
while not queue.empty():
qlist.append(queue.get())
return qlist