diff --git a/beeref/commands.py b/beeref/commands.py index 7fd8a50..1481fad 100644 --- a/beeref/commands.py +++ b/beeref/commands.py @@ -16,10 +16,10 @@ from PyQt6 import QtGui -class InsertImages(QtGui.QUndoCommand): +class InsertItems(QtGui.QUndoCommand): def __init__(self, scene, items): - super().__init__('Insert images') + super().__init__('Insert items') self.scene = scene self.items = items @@ -35,23 +35,26 @@ class InsertImages(QtGui.QUndoCommand): self.scene.removeItem(item) -class DeleteSelectedItems(QtGui.QUndoCommand): +class DeleteItems(QtGui.QUndoCommand): - def __init__(self, scene): - super().__init__('Delete images') + def __init__(self, scene, items): + super().__init__('Delete items') self.scene = scene - self.items = [] + self.items = items def redo(self): - for item in self.scene.selectedItems(): - self.items.append(item) + print(len(self.scene.items())) + for item in self.items: self.scene.removeItem(item) + print(len(self.scene.items())) def undo(self): + print(len(self.scene.items())) self.scene.clearSelection() for item in self.items: item.setSelected(True) self.scene.addItem(item) + print(len(self.scene.items())) class MoveItemsBy(QtGui.QUndoCommand): diff --git a/beeref/view.py b/beeref/view.py index 5baa59a..a4089b6 100644 --- a/beeref/view.py +++ b/beeref/view.py @@ -222,7 +222,8 @@ class BeeGraphicsView(QtWidgets.QGraphicsView): def on_action_delete_items(self): logger.debug('Deleting items...') - self.undo_stack.push(commands.DeleteSelectedItems(self.scene)) + self.undo_stack.push( + commands.DeleteItems(self.scene, self.scene.selectedItems())) def on_action_normalize_height(self): self.scene.normalize_height() @@ -241,7 +242,7 @@ class BeeGraphicsView(QtWidgets.QGraphicsView): item = BeePixmapItem(img, getattr(img, 'filename', None)) item.set_pos_center(img.pos.x(), img.pos.y()) items.append(item) - self.undo_stack.push(commands.InsertImages(self.scene, items)) + self.undo_stack.push(commands.InsertItems(self.scene, items)) def save_to_file(self, filename): logger.info(f'Saving to file {filename}') diff --git a/tests/test_commands.py b/tests/test_commands.py new file mode 100644 index 0000000..a8bdbbd --- /dev/null +++ b/tests/test_commands.py @@ -0,0 +1,136 @@ +from PyQt6 import QtGui + +from beeref import commands +from beeref.items import BeePixmapItem +from beeref.scene import BeeGraphicsScene +from .base import BeeTestCase + + +class InsertItemsTestCase(BeeTestCase): + + def test_redo_undo(self): + def get_images(): + return list(filter(lambda i: isinstance(i, BeePixmapItem), + scene.items())) + + scene = BeeGraphicsScene(None) + item1 = BeePixmapItem(QtGui.QImage()) + scene.addItem(item1) + item2 = BeePixmapItem(QtGui.QImage()) + command = commands.InsertItems(scene, [item2]) + command.redo() + assert len(get_images()) == 2 + assert item1 in scene.items() + assert item1.isSelected() is False + assert item2 in scene.items() + assert item2.isSelected() is True + command.undo() + assert get_images() == [item1] + assert item1.isSelected() is False + + +class DeleteItemsTestCase(BeeTestCase): + + def test_redo_undo(self): + def get_images(): + return list(filter(lambda i: isinstance(i, BeePixmapItem), + scene.items())) + + scene = BeeGraphicsScene(None) + 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 get_images() == [item1] + command.undo() + assert len(get_images()) == 2 + assert item1 in scene.items() + assert item1.isSelected() is False + assert item2 in scene.items() + assert item2.isSelected() is True + + +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], 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], 50, 100, 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 ScaleItemsByTestCase(BeeTestCase): + + def test_redo_undo(self): + item1 = BeePixmapItem(QtGui.QImage()) + item1.scale_factor = 1 + item2 = BeePixmapItem(QtGui.QImage()) + item2.scale_factor = 3 + command = commands.ScaleItemsBy([item1, item2], 2) + command.redo() + assert item1.scale_factor == 3 + assert item2.scale_factor == 5 + command.undo() + assert item1.scale_factor == 1 + assert item2.scale_factor == 3 + + def test_ignore_first_redo(self): + item1 = BeePixmapItem(QtGui.QImage()) + item1.scale_factor = 1 + item2 = BeePixmapItem(QtGui.QImage()) + item2.scale_factor = 3 + command = commands.ScaleItemsBy([item1, item2], 2, True) + command.redo() + assert item1.scale_factor == 1 + assert item2.scale_factor == 3 + command.redo() + assert item1.scale_factor == 3 + assert item2.scale_factor == 5 + + +class NormalizeItemsTestCase(BeeTestCase): + + def test_redo_undo(self): + item1 = BeePixmapItem(QtGui.QImage()) + item1.scale_factor = 1 + item2 = BeePixmapItem(QtGui.QImage()) + item2.scale_factor = 3 + command = commands.NormalizeItems([item1, item2], [2, 0.5]) + command.redo() + assert item1.scale_factor == 2 + assert item2.scale_factor == 0.5 + command.undo() + assert item1.scale_factor == 1 + assert item2.scale_factor == 3 diff --git a/tests/test_items.py b/tests/test_items.py index 8bccdfb..7e37127 100644 --- a/tests/test_items.py +++ b/tests/test_items.py @@ -1,3 +1,5 @@ +from unittest.mock import patch, PropertyMock + import os.path from PyQt6 import QtGui, QtWidgets @@ -20,6 +22,31 @@ class BeePixmapItemTestCase(BeeTestCase): | QtWidgets.QGraphicsItem.GraphicsItemFlags.ItemIsSelectable) assert item.filename == filename + def test_set_scale(self): + item = BeePixmapItem(QtGui.QImage()) + item.setScale(3) + assert item.scale_factor == 3 + + def test_set_scale_ignores_zero(self): + item = BeePixmapItem(QtGui.QImage()) + item.setScale(0) + assert item.scale_factor == 1 + + def test_set_scale_ignores_negative(self): + item = BeePixmapItem(QtGui.QImage()) + item.setScale(-0.1) + assert item.scale_factor == 1 + + 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(0, 0) + assert item.pos().x() == -100 + assert item.pos().y() == -50 + class BeePixmapItemToBeeJsonTestCase(BeeTestCase): diff --git a/tests/test_scene.py b/tests/test_scene.py index 5c62c22..009d8c3 100644 --- a/tests/test_scene.py +++ b/tests/test_scene.py @@ -24,12 +24,6 @@ class BeeGraphicsSceneNormalizeTestCase(BeeTestCase): item1.setScale.assert_called_once_with(1.5) item2.setScale.assert_called_once_with(0.75) - item1.setScale.reset_mock() - item2.setScale.reset_mock() - self.undo_stack.undo() - item1.setScale.assert_called_once_with(1) - item2.setScale.assert_called_once_with(3) - def test_normalize_width(self): item1 = MagicMock(width=100, scale_factor=1) item2 = MagicMock(width=200, scale_factor=3) @@ -41,12 +35,6 @@ class BeeGraphicsSceneNormalizeTestCase(BeeTestCase): item1.setScale.assert_called_once_with(1.5) item2.setScale.assert_called_once_with(0.75) - item1.setScale.reset_mock() - item2.setScale.reset_mock() - self.undo_stack.undo() - item1.setScale.assert_called_once_with(1) - item2.setScale.assert_called_once_with(3) - def test_normalize_size(self): item1 = MagicMock(width=100, height=200, scale_factor=1) item2 = MagicMock(width=400, height=100, scale_factor=3) @@ -57,9 +45,3 @@ class BeeGraphicsSceneNormalizeTestCase(BeeTestCase): item1.setScale.assert_called_once_with(math.sqrt(1.5)) item2.setScale.assert_called_once_with(math.sqrt(0.75)) - - item1.setScale.reset_mock() - item2.setScale.reset_mock() - self.undo_stack.undo() - item1.setScale.assert_called_once_with(1) - item2.setScale.assert_called_once_with(3) diff --git a/tests/test_view.py b/tests/test_view.py new file mode 100644 index 0000000..c64ead0 --- /dev/null +++ b/tests/test_view.py @@ -0,0 +1,17 @@ +from unittest.mock import patch + +from beeref.view import BeeGraphicsView +from .base import BeeTestCase + + +class BeeGraphicsViewTestCase(BeeTestCase): + + @patch('beeref.view.BeeGraphicsView.open_from_file') + def test_init_without_filename(self, open_file_mock): + BeeGraphicsView(self.app) + open_file_mock.assert_not_called() + + @patch('beeref.view.BeeGraphicsView.open_from_file') + def test_init_with_filename(self, open_file_mock): + BeeGraphicsView(self.app, filename='bee.png') + open_file_mock.assert_called_once_with('bee.png')