Enhance test coverage and refactor code in various modules
- Achieved 85% overall test coverage with detailed results for individual components. - Added unit tests for DragInterceptor and MainWindow components. - Refactored imports and removed unused code in multiple files. - Updated test configurations and ensured compliance with coverage standards.
This commit is contained in:
parent
736b80b8f1
commit
dbf8f2b92f
10 changed files with 793 additions and 18 deletions
337
tests/unit/test_drag_interceptor.py
Normal file
337
tests/unit/test_drag_interceptor.py
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
"""Unit tests for DragInterceptor component."""
|
||||
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
|
||||
from webdrop_bridge.core.drag_interceptor import DragInterceptor
|
||||
from webdrop_bridge.core.validator import PathValidator
|
||||
|
||||
|
||||
class TestDragInterceptorInitialization:
|
||||
"""Test DragInterceptor initialization and setup."""
|
||||
|
||||
def test_drag_interceptor_creation(self, qtbot):
|
||||
"""Test DragInterceptor can be instantiated."""
|
||||
interceptor = DragInterceptor()
|
||||
assert interceptor is not None
|
||||
assert interceptor._validator is None
|
||||
|
||||
def test_drag_interceptor_has_signals(self, qtbot):
|
||||
"""Test DragInterceptor has required signals."""
|
||||
interceptor = DragInterceptor()
|
||||
assert hasattr(interceptor, "drag_started")
|
||||
assert hasattr(interceptor, "drag_failed")
|
||||
|
||||
def test_set_validator(self, qtbot, tmp_path):
|
||||
"""Test setting validator on drag interceptor."""
|
||||
interceptor = DragInterceptor()
|
||||
validator = PathValidator([tmp_path])
|
||||
|
||||
interceptor.set_validator(validator)
|
||||
|
||||
assert interceptor._validator is validator
|
||||
|
||||
|
||||
class TestDragInterceptorValidation:
|
||||
"""Test path validation in drag operations."""
|
||||
|
||||
def test_initiate_drag_no_files(self, qtbot):
|
||||
"""Test initiating drag with no files fails."""
|
||||
interceptor = DragInterceptor()
|
||||
with qtbot.waitSignal(interceptor.drag_failed):
|
||||
result = interceptor.initiate_drag([])
|
||||
|
||||
assert result is False
|
||||
|
||||
def test_initiate_drag_no_validator(self, qtbot):
|
||||
"""Test initiating drag without validator fails."""
|
||||
interceptor = DragInterceptor()
|
||||
with qtbot.waitSignal(interceptor.drag_failed):
|
||||
result = interceptor.initiate_drag(["file.txt"])
|
||||
|
||||
assert result is False
|
||||
|
||||
def test_initiate_drag_single_valid_file(self, qtbot, tmp_path):
|
||||
"""Test initiating drag with single valid file."""
|
||||
# Create a test file
|
||||
test_file = tmp_path / "test.txt"
|
||||
test_file.write_text("test content")
|
||||
|
||||
interceptor = DragInterceptor()
|
||||
validator = PathValidator([tmp_path])
|
||||
interceptor.set_validator(validator)
|
||||
|
||||
# Mock the drag operation to simulate success
|
||||
with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
|
||||
mock_drag_instance = MagicMock()
|
||||
# Simulate successful copy action
|
||||
from PySide6.QtCore import Qt
|
||||
mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
|
||||
mock_drag.return_value = mock_drag_instance
|
||||
|
||||
result = interceptor.initiate_drag([str(test_file)])
|
||||
|
||||
# Should return True on successful drag
|
||||
assert result is True
|
||||
|
||||
def test_initiate_drag_invalid_path(self, qtbot, tmp_path):
|
||||
"""Test drag with invalid path fails."""
|
||||
interceptor = DragInterceptor()
|
||||
validator = PathValidator([tmp_path])
|
||||
interceptor.set_validator(validator)
|
||||
|
||||
# Path outside allowed roots
|
||||
invalid_path = Path("/etc/passwd")
|
||||
|
||||
with qtbot.waitSignal(interceptor.drag_failed):
|
||||
result = interceptor.initiate_drag([str(invalid_path)])
|
||||
|
||||
assert result is False
|
||||
|
||||
def test_initiate_drag_nonexistent_file(self, qtbot, tmp_path):
|
||||
"""Test drag with nonexistent file fails."""
|
||||
interceptor = DragInterceptor()
|
||||
validator = PathValidator([tmp_path])
|
||||
interceptor.set_validator(validator)
|
||||
|
||||
nonexistent = tmp_path / "nonexistent.txt"
|
||||
|
||||
with qtbot.waitSignal(interceptor.drag_failed):
|
||||
result = interceptor.initiate_drag([str(nonexistent)])
|
||||
|
||||
assert result is False
|
||||
|
||||
|
||||
class TestDragInterceptorMultipleFiles:
|
||||
"""Test drag operations with multiple files."""
|
||||
|
||||
def test_initiate_drag_multiple_files(self, qtbot, tmp_path):
|
||||
"""Test drag with multiple valid files."""
|
||||
# Create test files
|
||||
file1 = tmp_path / "file1.txt"
|
||||
file2 = tmp_path / "file2.txt"
|
||||
file1.write_text("content 1")
|
||||
file2.write_text("content 2")
|
||||
|
||||
interceptor = DragInterceptor()
|
||||
validator = PathValidator([tmp_path])
|
||||
interceptor.set_validator(validator)
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
|
||||
mock_drag_instance = MagicMock()
|
||||
mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
|
||||
mock_drag.return_value = mock_drag_instance
|
||||
|
||||
result = interceptor.initiate_drag([str(file1), str(file2)])
|
||||
|
||||
assert result is True
|
||||
|
||||
def test_initiate_drag_mixed_valid_invalid(self, qtbot, tmp_path):
|
||||
"""Test drag with mix of valid and invalid paths fails."""
|
||||
test_file = tmp_path / "valid.txt"
|
||||
test_file.write_text("content")
|
||||
|
||||
interceptor = DragInterceptor()
|
||||
validator = PathValidator([tmp_path])
|
||||
interceptor.set_validator(validator)
|
||||
|
||||
# Mix of valid and invalid paths
|
||||
with qtbot.waitSignal(interceptor.drag_failed):
|
||||
result = interceptor.initiate_drag(
|
||||
[str(test_file), "/etc/passwd"]
|
||||
)
|
||||
|
||||
assert result is False
|
||||
|
||||
|
||||
class TestDragInterceptorMimeData:
|
||||
"""Test MIME data creation and file URL formatting."""
|
||||
|
||||
def test_mime_data_creation(self, qtbot, tmp_path):
|
||||
"""Test MIME data is created with proper file URLs."""
|
||||
test_file = tmp_path / "test.txt"
|
||||
test_file.write_text("test")
|
||||
|
||||
interceptor = DragInterceptor()
|
||||
validator = PathValidator([tmp_path])
|
||||
interceptor.set_validator(validator)
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
|
||||
mock_drag_instance = MagicMock()
|
||||
mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
|
||||
mock_drag.return_value = mock_drag_instance
|
||||
|
||||
interceptor.initiate_drag([str(test_file)])
|
||||
|
||||
# Check MIME data was set correctly
|
||||
call_args = mock_drag_instance.setMimeData.call_args
|
||||
mime_data = call_args[0][0]
|
||||
|
||||
# Verify URLs were set
|
||||
urls = mime_data.urls()
|
||||
assert len(urls) == 1
|
||||
# Check that the URL contains file:// scheme (can be string repr or QUrl)
|
||||
url_str = str(urls[0]).lower()
|
||||
assert "file://" in url_str
|
||||
|
||||
|
||||
class TestDragInterceptorSignals:
|
||||
"""Test signal emission on drag operations."""
|
||||
|
||||
def test_drag_started_signal_emitted(self, qtbot, tmp_path):
|
||||
"""Test drag_started signal is emitted on success."""
|
||||
test_file = tmp_path / "test.txt"
|
||||
test_file.write_text("content")
|
||||
|
||||
interceptor = DragInterceptor()
|
||||
validator = PathValidator([tmp_path])
|
||||
interceptor.set_validator(validator)
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
# Connect to signal manually
|
||||
signal_spy = []
|
||||
interceptor.drag_started.connect(lambda paths: signal_spy.append(paths))
|
||||
|
||||
with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
|
||||
mock_drag_instance = MagicMock()
|
||||
mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
|
||||
mock_drag.return_value = mock_drag_instance
|
||||
|
||||
result = interceptor.initiate_drag([str(test_file)])
|
||||
|
||||
# Verify result and signal emission
|
||||
assert result is True
|
||||
assert len(signal_spy) == 1
|
||||
|
||||
def test_drag_failed_signal_on_no_files(self, qtbot):
|
||||
"""Test drag_failed signal on empty file list."""
|
||||
interceptor = DragInterceptor()
|
||||
|
||||
# Connect to signal manually
|
||||
signal_spy = []
|
||||
interceptor.drag_failed.connect(lambda msg: signal_spy.append(msg))
|
||||
|
||||
result = interceptor.initiate_drag([])
|
||||
|
||||
# Verify result and signal emission
|
||||
assert result is False
|
||||
assert len(signal_spy) == 1
|
||||
assert "No files" in signal_spy[0]
|
||||
|
||||
def test_drag_failed_signal_on_validation_error(self, qtbot, tmp_path):
|
||||
"""Test drag_failed signal on validation failure."""
|
||||
interceptor = DragInterceptor()
|
||||
validator = PathValidator([tmp_path])
|
||||
interceptor.set_validator(validator)
|
||||
|
||||
# Connect to signal manually
|
||||
signal_spy = []
|
||||
interceptor.drag_failed.connect(lambda msg: signal_spy.append(msg))
|
||||
|
||||
result = interceptor.initiate_drag(["/invalid/path/file.txt"])
|
||||
|
||||
# Verify result and signal emission
|
||||
assert result is False
|
||||
assert len(signal_spy) == 1
|
||||
|
||||
|
||||
class TestDragInterceptorDragExecution:
|
||||
"""Test drag operation execution and result handling."""
|
||||
|
||||
def test_drag_cancelled_returns_false(self, qtbot, tmp_path):
|
||||
"""Test drag cancellation returns False."""
|
||||
test_file = tmp_path / "test.txt"
|
||||
test_file.write_text("content")
|
||||
|
||||
interceptor = DragInterceptor()
|
||||
validator = PathValidator([tmp_path])
|
||||
interceptor.set_validator(validator)
|
||||
|
||||
with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
|
||||
mock_drag_instance = MagicMock()
|
||||
mock_drag_instance.exec.return_value = 0 # Cancelled/failed
|
||||
mock_drag.return_value = mock_drag_instance
|
||||
|
||||
# Connect to signal manually
|
||||
signal_spy = []
|
||||
interceptor.drag_failed.connect(lambda msg: signal_spy.append(msg))
|
||||
|
||||
result = interceptor.initiate_drag([str(test_file)])
|
||||
|
||||
assert result is False
|
||||
assert len(signal_spy) == 1
|
||||
|
||||
def test_pixmap_created_from_widget(self, qtbot, tmp_path):
|
||||
"""Test pixmap is created from widget grab."""
|
||||
test_file = tmp_path / "test.txt"
|
||||
test_file.write_text("content")
|
||||
|
||||
interceptor = DragInterceptor()
|
||||
validator = PathValidator([tmp_path])
|
||||
interceptor.set_validator(validator)
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
with patch.object(interceptor, "grab") as mock_grab:
|
||||
mock_pixmap = MagicMock()
|
||||
mock_grab.return_value.scaled.return_value = mock_pixmap
|
||||
|
||||
with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
|
||||
mock_drag_instance = MagicMock()
|
||||
mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
|
||||
mock_drag.return_value = mock_drag_instance
|
||||
|
||||
interceptor.initiate_drag([str(test_file)])
|
||||
|
||||
# Verify grab was called and pixmap was set
|
||||
mock_grab.assert_called_once()
|
||||
mock_drag_instance.setPixmap.assert_called_once_with(mock_pixmap)
|
||||
|
||||
|
||||
class TestDragInterceptorIntegration:
|
||||
"""Integration tests with PathValidator."""
|
||||
|
||||
def test_drag_with_nested_file(self, qtbot, tmp_path):
|
||||
"""Test drag with file in nested directory."""
|
||||
nested_dir = tmp_path / "nested" / "dir"
|
||||
nested_dir.mkdir(parents=True)
|
||||
test_file = nested_dir / "file.txt"
|
||||
test_file.write_text("nested content")
|
||||
|
||||
interceptor = DragInterceptor()
|
||||
validator = PathValidator([tmp_path])
|
||||
interceptor.set_validator(validator)
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
|
||||
mock_drag_instance = MagicMock()
|
||||
mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
|
||||
mock_drag.return_value = mock_drag_instance
|
||||
|
||||
result = interceptor.initiate_drag([str(test_file)])
|
||||
|
||||
assert result is True
|
||||
|
||||
def test_drag_with_relative_path(self, qtbot, tmp_path):
|
||||
"""Test drag with relative path resolution."""
|
||||
test_file = tmp_path / "relative.txt"
|
||||
test_file.write_text("content")
|
||||
|
||||
interceptor = DragInterceptor()
|
||||
validator = PathValidator([tmp_path])
|
||||
interceptor.set_validator(validator)
|
||||
|
||||
# This would work if run from the directory, but we'll just verify
|
||||
# the interceptor handles Path objects correctly
|
||||
from PySide6.QtCore import Qt
|
||||
with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
|
||||
mock_drag_instance = MagicMock()
|
||||
mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
|
||||
mock_drag.return_value = mock_drag_instance
|
||||
|
||||
# Direct absolute path for reliable test
|
||||
result = interceptor.initiate_drag([str(test_file)])
|
||||
|
||||
assert result is True
|
||||
Loading…
Add table
Add a link
Reference in a new issue