""" test_integration.py — Step 8: End-to-end integration tests. Tests the full workflow: Load LDF → edit signals → start scheduler → see Rx → stop Verifies all components work together correctly. """ import sys import pytest from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent / "src")) from PyQt6.QtWidgets import QApplication from PyQt6.QtCore import Qt from PyQt6.QtTest import QTest SAMPLE_LDF = str(Path(__file__).parent.parent.parent / "resources" / "sample.ldf") @pytest.fixture(scope="session") def app(): application = QApplication.instance() or QApplication(sys.argv) yield application @pytest.fixture def window(app): from main_window import MainWindow w = MainWindow() return w class TestFullWorkflow: """Test the complete user workflow from start to finish.""" def test_load_edit_run_stop(self, window): """ Complete flow: 1. Load LDF 2. Edit a signal value 3. Start scheduler 4. Verify Rx data arrives 5. Stop scheduler """ # 1. Load LDF window._load_ldf_file(SAMPLE_LDF) assert window._ldf_data is not None assert window.tx_table.topLevelItemCount() == 2 assert window.rx_table.topLevelItemCount() == 2 # 2. Edit a signal — set MotorSpeed to 200 frame_item = window.tx_table.topLevelItem(0) # Motor_Command speed_sig = frame_item.child(2) # MotorSpeed speed_sig.setText(4, "200") # Verify frame bytes updated frame_data = frame_item.data(0, Qt.ItemDataRole.UserRole) assert frame_data['bytes'][1] == 200 # 3. Start scheduler window.combo_schedule.setCurrentIndex(0) window._on_start_scheduler() assert window._scheduler.is_running # 4. Wait for Rx data QTest.qWait(100) # Verify Rx timestamps appeared has_rx = False for i in range(window.rx_table.topLevelItemCount()): if window.rx_table.topLevelItem(i).text(0) != "—": has_rx = True break assert has_rx, "Should have received mock Rx data" # 5. Stop window._on_stop_scheduler() assert not window._scheduler.is_running def test_hex_dec_toggle_during_run(self, window): """Toggle hex/dec while scheduler is running.""" window._load_ldf_file(SAMPLE_LDF) window.combo_schedule.setCurrentIndex(0) window._on_start_scheduler() QTest.qWait(50) # Toggle to decimal window.chk_hex_mode.setChecked(False) QTest.qWait(50) # Toggle back to hex window.chk_hex_mode.setChecked(True) QTest.qWait(50) window._on_stop_scheduler() # No crash = pass def test_pause_resume_during_run(self, window): """Pause and resume the scheduler.""" window._load_ldf_file(SAMPLE_LDF) window.combo_schedule.setCurrentIndex(0) window._on_start_scheduler() QTest.qWait(50) # Pause window._on_pause_scheduler() assert window._scheduler.is_paused QTest.qWait(50) # Resume window._on_pause_scheduler() assert not window._scheduler.is_paused QTest.qWait(50) window._on_stop_scheduler() def test_clear_rx_during_run(self, window): """Clear Rx data while scheduler is running.""" window._load_ldf_file(SAMPLE_LDF) window.combo_schedule.setCurrentIndex(0) window._on_start_scheduler() QTest.qWait(100) window._on_clear_rx() # All timestamps should be reset for i in range(window.rx_table.topLevelItemCount()): assert window.rx_table.topLevelItem(i).text(0) == "—" # But scheduler should still be running assert window._scheduler.is_running window._on_stop_scheduler() def test_change_global_rate_live(self, window): """Change global rate while scheduler is running.""" window._load_ldf_file(SAMPLE_LDF) window.combo_schedule.setCurrentIndex(0) window._on_start_scheduler() QTest.qWait(50) window.spin_global_rate.setValue(10) QTest.qWait(50) window.spin_global_rate.setValue(100) QTest.qWait(50) window._on_stop_scheduler() def test_reload_ldf_stops_scheduler(self, window): """Reloading LDF while scheduler runs should stop first.""" window._load_ldf_file(SAMPLE_LDF) window.combo_schedule.setCurrentIndex(0) window._on_start_scheduler() QTest.qWait(50) # Reload — scheduler should handle this gracefully window._load_ldf_file(SAMPLE_LDF) # Tables should still be populated assert window.tx_table.topLevelItemCount() == 2 class TestBackendIntegration: """Test BabyLinBackend integration (mock mode).""" def test_backend_exists(self, window): assert window._backend is not None assert window._backend.is_mock_mode def test_backend_scan_from_gui(self, window): """Backend scan should return mock device.""" devices = window._backend.scan_devices() assert len(devices) >= 1 class TestEdgeCase: """Test edge cases and error conditions.""" def test_start_without_ldf(self, window, monkeypatch): """Starting scheduler without LDF loaded should not crash.""" from PyQt6.QtWidgets import QMessageBox monkeypatch.setattr(QMessageBox, "warning", lambda *args: None) window._on_start_scheduler() assert not window._scheduler.is_running def test_manual_send_without_selection(self, window, monkeypatch): """Manual send without selecting a frame should warn.""" from PyQt6.QtWidgets import QMessageBox warned = [] monkeypatch.setattr(QMessageBox, "warning", lambda *args: warned.append(True)) window._load_ldf_file(SAMPLE_LDF) window._on_manual_send() assert len(warned) > 0 def test_double_stop(self, window): """Stopping when already stopped should not crash.""" window._load_ldf_file(SAMPLE_LDF) window._on_stop_scheduler() window._on_stop_scheduler() # No crash