Files
radar_system/python_app/tests/test_live_settings_regressions.py
T
2026-10-06 12:32:26 +03:00

95 lines
5.8 KiB
Python

"""Settings reach the processor atomically and preserve the saved frequency band."""
from copy import deepcopy
import unittest
from unittest import mock
from PyQt6.QtWidgets import QApplication, QComboBox, QDoubleSpinBox, QStackedWidget
from python_app.gui.antenna_geometry_editor import AntennaGeometryEditor
from python_app.gui.controllers.sections.processing_section import build_processing_group
from python_app.models.gui_profile_schema import GuiStateModel
from python_app.models.dataset_model import SweepCollection
from python_app.models.run_config_schema import RunConfigModel
from python_app.tests.gui_test_support import isolated_window, widget
class LiveSettingsRegressionTest(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.app = QApplication.instance() or QApplication([])
def test_initial_processing_band_uses_sweep_limits_before_profile_values(self) -> None:
config, gui = RunConfigModel(), GuiStateModel()
config.radar.sweep.start_hz, config.radar.sweep.stop_hz = 1e6, 9.5e9
for settings in (gui.processing.bscan, gui.processing.gpr,
gui.processing.legacy_gpr, gui.processing.sar3d):
settings.start_freq_mhz, settings.stop_freq_mhz = 20, 9200
section = build_processing_group(config, gui)
self.addCleanup(section.group.deleteLater)
for mode in ("bscan", "gpr", "legacy_gpr", "sar3d"):
for bound, expected in (("start", 20), ("stop", 9200)):
with self.subTest(mode=mode, bound=bound):
editor = widget(section, f"_{mode}_{bound}_freq_mhz", QDoubleSpinBox)
self.assertEqual((editor.minimum(), editor.maximum(), editor.value()), (1, 9500, expected))
def test_failed_live_write_restores_web_batch_and_desktop_changes(self) -> None:
window = isolated_window(self.addCleanup)
window._pipeline.timer.stop()
mode = window._view.processing.mode
gain = widget(window._view.processing, "_bscan_gain", QDoubleSpinBox)
pages = window._view.processing.pages
live = window._live
before = (window._profile.build_gui_state(), window._config.geometry(),
live.active_mode, pages.currentIndex(), window._plots.stack.currentWidget(),
live.revision, deepcopy(live.processing_settings), window._plots.required_revision,
live.writer.path.read_bytes())
for source in ("web", "web_geometry", "desktop_mode", "desktop_value", "desktop_geometry"):
with self.subTest(source=source), \
mock.patch.object(live.writer, "write", side_effect=OSError("disk full")), \
mock.patch.object(window._preprocess, "invalidate_object_calibration_after_geometry_change") as invalidate, \
mock.patch.object(window._log, "show_exception") as error:
if source == "web":
live.apply_web({"processor_mode": "bscan", "bscan_gain": gain.value() + .1})
elif source in {"web_geometry", "desktop_geometry"}:
editor = window._view.processing.geometries[0]
records = editor.records()
records[0]["x_m"] += .125
if source == "web_geometry":
live.apply_web({"tx_geometry": records})
else:
editor.set_records(records)
elif source == "desktop_mode":
mode.setCurrentText("bscan")
else:
widget(window._view.processing, "_gpr_range_comp_power", QDoubleSpinBox).setValue(.4)
error.assert_called_once()
invalidate.assert_not_called()
self.assertEqual((window._profile.build_gui_state(), window._config.geometry(),
live.active_mode, pages.currentIndex(), window._plots.stack.currentWidget(),
live.revision, live.processing_settings, window._plots.required_revision,
live.writer.path.read_bytes()), before)
with mock.patch.object(window._plots, "draw_bscan", side_effect=ValueError("invalid plot")), \
mock.patch.object(window._log, "show_exception") as error:
live.apply_web({"processor_mode": "bscan", "bscan_gain": gain.value() + .1})
error.assert_called_once()
self.assertEqual((mode.currentText(), live.active_mode, pages.currentIndex()), ("bscan", "bscan", 1))
self.assertEqual(live.processing_settings[live.revision]["processor_mode"], "bscan")
self.assertNotEqual(live.writer.path.read_bytes(), before[-1])
def test_mode_publication_selects_replay_from_new_mode_before_changing_the_plot(self) -> None:
window = isolated_window(self.addCleanup)
window._pipeline.timer.stop()
window._history.preprocessed.append(SweepCollection(1, 1, [], capture_start_ns=1, capture_end_ns=2))
with mock.patch.object(window._pipeline.supervisor, "is_processor_running", return_value=True), \
mock.patch.object(window._pipeline, "_result_reader"), \
mock.patch.object(window._pipeline, "drain_results", return_value=None), \
mock.patch.object(window._pipeline, "schedule_replay"), \
mock.patch.object(window._plots, "draw_bscan"):
for mode, reprocess in (("bscan", False), ("pass_through", True)):
with self.subTest(mode=mode):
window._live.apply_web({"processor_mode": mode})
published = window._live.processing_settings[window._live.revision]
self.assertEqual(published["processor_mode"], mode)
self.assertEqual(published["reprocess_current_result"], reprocess)