EU-Utility/tests/test_clipboard.py

275 lines
8.4 KiB
Python

"""
Test suite for Clipboard Manager
Run with: python -m pytest tests/test_clipboard.py -v
Or: python tests/test_clipboard.py
"""
import json
import os
import sys
import tempfile
import threading
import time
from pathlib import Path
# Add parent to path
sys.path.insert(0, str(Path(__file__).parent.parent))
from core.clipboard import ClipboardManager, ClipboardEntry, get_clipboard_manager
class TestClipboardManager:
"""Tests for ClipboardManager functionality."""
def setup_method(self):
"""Setup for each test - use temp file for history."""
# Reset singleton for clean tests
ClipboardManager._instance = None
self.temp_dir = tempfile.mkdtemp()
self.history_file = os.path.join(self.temp_dir, "test_history.json")
self.manager = ClipboardManager(history_file=self.history_file)
def teardown_method(self):
"""Cleanup after each test."""
self.manager.stop_monitoring()
ClipboardManager._instance = None
# Clean up temp file
if os.path.exists(self.history_file):
os.remove(self.history_file)
os.rmdir(self.temp_dir)
def test_singleton(self):
"""Test that ClipboardManager is a singleton."""
manager1 = get_clipboard_manager()
manager2 = get_clipboard_manager()
assert manager1 is manager2
def test_copy_and_paste(self):
"""Test basic copy and paste functionality."""
test_text = "Hello, Clipboard!"
# Copy
result = self.manager.copy(test_text)
assert result is True
# Paste
pasted = self.manager.paste()
assert pasted == test_text
def test_history_tracking(self):
"""Test that copies are tracked in history."""
texts = ["First", "Second", "Third"]
for text in texts:
self.manager.copy(text, source="test")
time.sleep(0.05) # Small delay for timestamp differences
history = self.manager.get_history()
# Should have 3 entries (most recent first)
assert len(history) == 3
assert history[0].content == "Third"
assert history[1].content == "Second"
assert history[2].content == "First"
def test_history_limit(self):
"""Test that history is limited to MAX_HISTORY_SIZE."""
# Copy more than max entries
for i in range(110):
self.manager.copy(f"Entry {i}")
history = self.manager.get_history()
assert len(history) == 100 # MAX_HISTORY_SIZE
def test_no_duplicate_consecutive_entries(self):
"""Test that duplicate consecutive entries aren't added."""
self.manager.copy("Same text")
self.manager.copy("Same text")
self.manager.copy("Same text")
history = self.manager.get_history()
assert len(history) == 1
def test_history_persistence(self):
"""Test that history is saved and loaded correctly."""
# Add some entries
self.manager.copy("Persistent", source="test")
time.sleep(0.1) # Let async save complete
# Force save
self.manager._save_history()
# Verify file exists and has content
assert os.path.exists(self.history_file)
with open(self.history_file, 'r') as f:
data = json.load(f)
assert 'history' in data
assert len(data['history']) >= 1
def test_clear_history(self):
"""Test clearing history."""
self.manager.copy("To be cleared")
assert len(self.manager.get_history()) == 1
self.manager.clear_history()
assert len(self.manager.get_history()) == 0
def test_thread_safety(self):
"""Test thread-safe operations."""
errors = []
def copy_worker(text):
try:
for i in range(10):
self.manager.copy(f"{text}_{i}")
except Exception as e:
errors.append(e)
# Run multiple threads
threads = []
for i in range(5):
t = threading.Thread(target=copy_worker, args=(f"Thread{i}",))
threads.append(t)
t.start()
for t in threads:
t.join()
assert len(errors) == 0, f"Thread errors: {errors}"
def test_has_changed(self):
"""Test clipboard change detection."""
# Initial state
self.manager.copy("Initial")
assert self.manager.has_changed() is False # Same content
# Manually simulate external change (mock by copying new content)
self.manager.copy("Changed")
# Note: has_changed() compares to _last_content which is updated on copy
# So we need to simulate external change differently
old_content = self.manager._last_content
self.manager._last_content = "Different"
assert self.manager.has_changed() is True
def test_get_history_limit(self):
"""Test getting limited history."""
for i in range(10):
self.manager.copy(f"Entry {i}")
limited = self.manager.get_history(limit=5)
assert len(limited) == 5
def test_entry_metadata(self):
"""Test that entries have correct metadata."""
self.manager.copy("Test content", source="my_plugin")
history = self.manager.get_history()
entry = history[0]
assert entry.content == "Test content"
assert entry.source == "my_plugin"
assert entry.timestamp is not None
def test_monitoring(self):
"""Test clipboard monitoring."""
changes = []
def on_change(content):
changes.append(content)
self.manager.start_monitoring(callback=on_change)
assert self.manager.is_monitoring() is True
time.sleep(0.1)
self.manager.stop_monitoring()
assert self.manager.is_monitoring() is False
def test_stats(self):
"""Test getting stats."""
self.manager.copy("Stat test")
stats = self.manager.get_stats()
assert 'history_count' in stats
assert 'max_history' in stats
assert 'is_available' in stats
assert stats['max_history'] == 100
class TestClipboardEntry:
"""Tests for ClipboardEntry dataclass."""
def test_to_dict(self):
"""Test conversion to dictionary."""
entry = ClipboardEntry(
content="Test",
timestamp="2024-01-01T00:00:00",
source="test_plugin"
)
d = entry.to_dict()
assert d['content'] == "Test"
assert d['timestamp'] == "2024-01-01T00:00:00"
assert d['source'] == "test_plugin"
def test_from_dict(self):
"""Test creation from dictionary."""
data = {
'content': 'Test',
'timestamp': '2024-01-01T00:00:00',
'source': 'test_plugin'
}
entry = ClipboardEntry.from_dict(data)
assert entry.content == "Test"
assert entry.timestamp == "2024-01-01T00:00:00"
assert entry.source == "test_plugin"
def run_manual_test():
"""Run a manual demonstration of clipboard features."""
print("\n" + "="*60)
print("MANUAL CLIPBOARD MANAGER TEST")
print("="*60)
# Reset singleton
ClipboardManager._instance = None
manager = get_clipboard_manager()
print(f"\n1. Clipboard available: {manager.is_available()}")
# Copy test
print("\n2. Copying text to clipboard...")
manager.copy("Hello from EU-Utility!")
print(" Copied: 'Hello from EU-Utility!'")
# Paste test
print("\n3. Reading from clipboard...")
pasted = manager.paste()
print(f" Pasted: '{pasted}'")
# History test
print("\n4. Adding more entries...")
manager.copy("Coordinates: 100, 200", source="test")
manager.copy("GPS: 45.5231, -122.6765", source="test")
manager.copy("Position: x=50, y=100", source="test")
print("\n5. History (last 5):")
for i, entry in enumerate(manager.get_history(limit=5), 1):
print(f" {i}. [{entry.source}] {entry.content[:40]}")
print("\n6. Stats:")
stats = manager.get_stats()
for key, value in stats.items():
print(f" {key}: {value}")
print("\n" + "="*60)
print("TEST COMPLETE")
print("="*60)
if __name__ == "__main__":
run_manual_test()