| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 |
- """
- Tests for ProgressWidget UI component.
- """
- import pytest
- # Skip tests if PyQt6 is not installed
- pytest.importorskip("PyQt6")
- from datetime import datetime, timedelta
- from unittest.mock import Mock
- from PyQt6.QtWidgets import QApplication
- from PyQt6.QtCore import Qt, QTimer
- from PyQt6.QtTest import QTest
- from src.ui.progress_widget import ProgressWidget, ChapterProgressItem
- from src.scheduler.progress import ProgressNotifier
- from src.scheduler.models import ChapterTask, PipelineProgress, TaskStatus, SchedulerState
- @pytest.fixture
- def app(qtbot):
- """Create QApplication fixture."""
- test_app = QApplication.instance()
- if test_app is None:
- test_app = QApplication([])
- yield test_app
- @pytest.fixture
- def progress_widget(app, qtbot):
- """Create ProgressWidget fixture."""
- widget = ProgressWidget()
- qtbot.addWidget(widget)
- yield widget
- widget.close()
- @pytest.fixture
- def sample_chapter_tasks():
- """Create sample chapter tasks for testing."""
- return [
- ChapterTask(
- chapter_id=f"ch_{i}",
- chapter_index=i,
- title=f"Chapter {i + 1}",
- original_content=f"Content for chapter {i + 1}\n",
- )
- for i in range(5)
- ]
- class TestProgressWidget:
- """Test ProgressWidget functionality."""
- def test_initialization(self, progress_widget):
- """Test progress widget initializes correctly."""
- assert progress_widget._progress is None
- assert progress_widget._start_time is None
- assert len(progress_widget._chapter_items) == 0
- assert not progress_widget._eta_timer.isActive()
- def test_initial_ui_state(self, progress_widget):
- """Test initial UI state."""
- assert "No active translation" in progress_widget._stage_label.text()
- assert progress_widget._progress_bar.value() == 0
- assert progress_widget._completed_label.text() == "Completed: 0"
- assert progress_widget._failed_label.text() == "Failed: 0"
- assert progress_widget._remaining_label.text() == "Remaining: 0"
- def test_connect_to_notifier(self, progress_widget):
- """Test connecting to ProgressNotifier."""
- notifier = ProgressNotifier()
- progress_widget.connect_to_notifier(notifier)
- assert notifier.observer_count == 1
- def test_disconnect_from_notifier(self, progress_widget):
- """Test disconnecting from ProgressNotifier."""
- notifier = ProgressNotifier()
- progress_widget.connect_to_notifier(notifier)
- assert notifier.observer_count == 1
- progress_widget.disconnect_from_notifier(notifier)
- assert notifier.observer_count == 0
- def test_on_pipeline_start(self, progress_widget, sample_chapter_tasks):
- """Test pipeline start event."""
- notifier = ProgressNotifier()
- progress_widget.connect_to_notifier(notifier)
- notifier.notify_pipeline_start(5)
- assert progress_widget._progress is not None
- assert progress_widget._progress.total_chapters == 5
- assert progress_widget._progress.state == SchedulerState.RUNNING
- assert progress_widget._start_time is not None
- assert progress_widget._eta_timer.isActive()
- def test_on_pipeline_complete(self, progress_widget):
- """Test pipeline complete event."""
- notifier = ProgressNotifier()
- progress_widget.connect_to_notifier(notifier)
- # Start the pipeline
- notifier.notify_pipeline_start(5)
- # Complete the pipeline
- progress = PipelineProgress(
- total_chapters=5,
- completed_chapters=5,
- state=SchedulerState.COMPLETED
- )
- notifier.notify_pipeline_complete(progress)
- assert not progress_widget._eta_timer.isActive()
- assert progress_widget._progress_bar.value() == 100
- def test_on_pipeline_paused(self, progress_widget):
- """Test pipeline paused event."""
- notifier = ProgressNotifier()
- progress_widget.connect_to_notifier(notifier)
- progress = PipelineProgress(
- total_chapters=5,
- completed_chapters=2,
- current_chapter=2,
- state=SchedulerState.PAUSED
- )
- notifier.notify_pipeline_paused(progress)
- assert "paused" in progress_widget._stage_label.text().lower()
- def test_on_pipeline_resumed(self, progress_widget):
- """Test pipeline resumed event."""
- notifier = ProgressNotifier()
- progress_widget.connect_to_notifier(notifier)
- progress = PipelineProgress(
- total_chapters=5,
- completed_chapters=2,
- current_chapter=2,
- state=SchedulerState.RUNNING
- )
- notifier.notify_pipeline_resumed(progress)
- assert progress_widget._eta_timer.isActive()
- def test_on_chapter_start(self, progress_widget, sample_chapter_tasks):
- """Test chapter start event."""
- notifier = ProgressNotifier()
- progress_widget.connect_to_notifier(notifier)
- notifier.notify_pipeline_start(5)
- notifier.notify_chapter_start(sample_chapter_tasks[0])
- assert len(progress_widget._chapter_items) == 1
- def test_on_chapter_complete(self, progress_widget, sample_chapter_tasks):
- """Test chapter complete event."""
- notifier = ProgressNotifier()
- progress_widget.connect_to_notifier(notifier)
- notifier.notify_pipeline_start(5)
- # Mark task as completed
- task = sample_chapter_tasks[0]
- task.status = TaskStatus.COMPLETED
- notifier.notify_chapter_complete(task)
- assert progress_widget._progress.completed_chapters == 1
- def test_on_chapter_failed(self, progress_widget, sample_chapter_tasks):
- """Test chapter failed event."""
- notifier = ProgressNotifier()
- progress_widget.connect_to_notifier(notifier)
- notifier.notify_pipeline_start(5)
- # Mark task as failed
- task = sample_chapter_tasks[0]
- task.status = TaskStatus.FAILED
- task.error_message = "Test error"
- notifier.notify_chapter_failed(task, "Test error")
- assert progress_widget._progress.failed_chapters == 1
- def test_multiple_chapters(self, progress_widget, sample_chapter_tasks):
- """Test handling multiple chapters."""
- notifier = ProgressNotifier()
- progress_widget.connect_to_notifier(notifier)
- notifier.notify_pipeline_start(5)
- # Start and complete 3 chapters
- for i in range(3):
- task = sample_chapter_tasks[i]
- notifier.notify_chapter_start(task)
- task.status = TaskStatus.COMPLETED
- notifier.notify_chapter_complete(task)
- assert len(progress_widget._chapter_items) == 3
- assert progress_widget._progress.completed_chapters == 3
- def test_stats_update(self, progress_widget):
- """Test statistics display updates."""
- notifier = ProgressNotifier()
- progress_widget.connect_to_notifier(notifier)
- notifier.notify_pipeline_start(10)
- progress = PipelineProgress(
- total_chapters=10,
- completed_chapters=5,
- failed_chapters=1,
- state=SchedulerState.RUNNING
- )
- progress_widget._progress = progress
- progress_widget._update_stats()
- assert progress_widget._completed_label.text() == "Completed: 5"
- assert progress_widget._failed_label.text() == "Failed: 1"
- assert progress_widget._remaining_label.text() == "Remaining: 4"
- def test_reset(self, progress_widget):
- """Test resetting the widget."""
- notifier = ProgressNotifier()
- progress_widget.connect_to_notifier(notifier)
- # Start and add some chapters
- notifier.notify_pipeline_start(5)
- task = ChapterTask(
- chapter_id="ch_0",
- chapter_index=0,
- title="Chapter 1",
- original_content="Content\n"
- )
- notifier.notify_chapter_start(task)
- assert len(progress_widget._chapter_items) == 1
- # Reset
- progress_widget.reset()
- assert progress_widget._progress is None
- assert progress_widget._start_time is None
- assert len(progress_widget._chapter_items) == 0
- assert not progress_widget._eta_timer.isActive()
- def test_format_time(self, progress_widget):
- """Test time formatting."""
- # Seconds only
- assert progress_widget._format_time(45) == "00:45"
- # Minutes and seconds
- assert progress_widget._format_time(125) == "02:05"
- # Hours, minutes, seconds
- assert progress_widget._format_time(3661) == "01:01:01"
- class TestChapterProgressItem:
- """Test ChapterProgressItem functionality."""
- def test_initialization(self, app):
- """Test chapter progress item initialization."""
- task = ChapterTask(
- chapter_id="ch_0",
- chapter_index=0,
- title="Chapter 1",
- original_content="Content\n"
- )
- item = ChapterProgressItem(task)
- assert item._task == task
- assert item._title_label.text() == "Chapter 1: Chapter 1"
- def test_status_icons(self, app):
- """Test status icons are correct."""
- task = ChapterTask(
- chapter_id="ch_0",
- chapter_index=0,
- title="Chapter 1",
- original_content="Content\n"
- )
- item = ChapterProgressItem(task)
- # Test different statuses
- for status, expected_icon in ChapterProgressItem.STATUS_ICONS.items():
- task.status = status
- item._update_style()
- assert item._icon_label.text() == expected_icon
- def test_update_task(self, app):
- """Test updating task in item."""
- task = ChapterTask(
- chapter_id="ch_0",
- chapter_index=0,
- title="Chapter 1",
- original_content="Content\n"
- )
- item = ChapterProgressItem(task)
- # Update task with completion
- task.status = TaskStatus.COMPLETED
- task.started_at = datetime.now()
- task.completed_at = datetime.now() + timedelta(seconds=10)
- item.update_task(task)
- assert item._task.status == TaskStatus.COMPLETED
- assert "Completed" in item._details_label.text()
- def test_failed_task_display(self, app):
- """Test failed task displays error message."""
- task = ChapterTask(
- chapter_id="ch_0",
- chapter_index=0,
- title="Chapter 1",
- original_content="Content\n",
- status=TaskStatus.FAILED,
- error_message="Translation failed: connection timeout"
- )
- item = ChapterProgressItem(task)
- assert "Failed" in item._details_label.text()
- assert "connection timeout" in item._details_label.text()
- def test_retrying_task_display(self, app):
- """Test retrying task displays retry count."""
- task = ChapterTask(
- chapter_id="ch_0",
- chapter_index=0,
- title="Chapter 1",
- original_content="Content\n",
- status=TaskStatus.RETRYING,
- retry_count=2
- )
- item = ChapterProgressItem(task)
- assert "Retrying" in item._details_label.text()
- assert "2/3" in item._details_label.text()
- class TestProgressIntegration:
- """Test ProgressWidget integration with ProgressNotifier."""
- def test_full_workflow(self, progress_widget, sample_chapter_tasks):
- """Test complete translation workflow."""
- notifier = ProgressNotifier()
- progress_widget.connect_to_notifier(notifier)
- # Start pipeline
- notifier.notify_pipeline_start(5)
- assert progress_widget._progress.total_chapters == 5
- # Process chapters
- for i, task in enumerate(sample_chapter_tasks):
- notifier.notify_chapter_start(task)
- # Simulate completion
- task.status = TaskStatus.COMPLETED
- task.started_at = datetime.now()
- task.completed_at = datetime.now() + timedelta(seconds=10)
- notifier.notify_chapter_complete(task)
- # Complete pipeline
- progress = progress_widget._progress
- notifier.notify_pipeline_complete(progress)
- assert progress_widget._progress_bar.value() == 100
- def test_error_recovery(self, progress_widget, sample_chapter_tasks):
- """Test error and recovery scenario."""
- notifier = ProgressNotifier()
- progress_widget.connect_to_notifier(notifier)
- # Start
- notifier.notify_pipeline_start(3)
- # First chapter succeeds
- notifier.notify_chapter_start(sample_chapter_tasks[0])
- sample_chapter_tasks[0].status = TaskStatus.COMPLETED
- notifier.notify_chapter_complete(sample_chapter_tasks[0])
- # Second chapter fails
- notifier.notify_chapter_start(sample_chapter_tasks[1])
- sample_chapter_tasks[1].status = TaskStatus.FAILED
- sample_chapter_tasks[1].error_message = "Network error"
- notifier.notify_chapter_failed(sample_chapter_tasks[1], "Network error")
- assert progress_widget._progress.completed_chapters == 1
- assert progress_widget._progress.failed_chapters == 1
|