refactor: implement thread-safe hardware operations, port caching, and refined state management in the device controller
This commit is contained in:
@@ -242,6 +242,11 @@ class TestRemoveTrailingZeros:
|
||||
remove_trailing_zeros(data)
|
||||
assert len(data) == 2
|
||||
|
||||
def test_preserves_negative_temperatures(self):
|
||||
data = [["25.0", "24.5"], ["-5.5", "-10.0"]]
|
||||
remove_trailing_zeros(data)
|
||||
assert len(data) == 2
|
||||
|
||||
|
||||
# ── save_to_csv ───────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -87,31 +87,48 @@ def test_clear_session(controller):
|
||||
assert controller.selectedPort == ""
|
||||
assert controller.lastWaferInfo == ["", "", 0, "", 0, 0]
|
||||
|
||||
def test_erase_memory(controller):
|
||||
controller._service.erase_wafer = MagicMock(return_value=True)
|
||||
def test_erase_memory_spawns_thread(controller):
|
||||
with patch("threading.Thread") as mock_thread:
|
||||
controller.eraseMemory("COM1")
|
||||
mock_thread.assert_called_once()
|
||||
assert controller.operationInProgress
|
||||
|
||||
def test_erase_memory_handler(controller):
|
||||
emitted_result = None
|
||||
def on_erase_result(result):
|
||||
nonlocal emitted_result
|
||||
emitted_result = result
|
||||
|
||||
controller.eraseResult.connect(on_erase_result)
|
||||
controller.eraseMemory("COM1")
|
||||
controller._handle_erase_finished(True)
|
||||
assert emitted_result == {"success": True}
|
||||
assert controller.connectionStatus == "Connected"
|
||||
assert not controller.operationInProgress
|
||||
|
||||
def test_read_debug(controller):
|
||||
controller._service.read_wafer_data = MagicMock(return_value=bytes([12] * 512))
|
||||
def test_read_debug_spawns_thread(controller):
|
||||
with patch("threading.Thread") as mock_thread:
|
||||
controller.readDebug("COM1")
|
||||
mock_thread.assert_called_once()
|
||||
assert controller.operationInProgress
|
||||
|
||||
def test_read_debug_handler(controller):
|
||||
emitted_result = None
|
||||
def on_debug_result(result):
|
||||
nonlocal emitted_result
|
||||
emitted_result = result
|
||||
|
||||
controller.debugResult.connect(on_debug_result)
|
||||
controller.readDebug("COM1")
|
||||
controller._handle_debug_finished({
|
||||
"success": True,
|
||||
"sensor_bytes": 512,
|
||||
"debug_bytes": 512
|
||||
})
|
||||
assert emitted_result is not None
|
||||
assert emitted_result.get("success") is True
|
||||
assert emitted_result.get("sensor_bytes") == 512
|
||||
assert emitted_result.get("debug_bytes") == 512
|
||||
assert controller.connectionStatus == "Connected"
|
||||
assert not controller.operationInProgress
|
||||
|
||||
def test_parse_and_save_data_and_get_chart_data(controller):
|
||||
res = controller.getChartData()
|
||||
@@ -187,3 +204,29 @@ def test_detect_wafer_clears_old_session(controller):
|
||||
assert len(controller._activity_log) == 1
|
||||
assert "Scanning all ports" in controller._activity_log[0]
|
||||
|
||||
def test_device_service_port_caching(controller):
|
||||
service = controller._service
|
||||
# Mock comports list
|
||||
with patch("serial.tools.list_ports.comports", return_value=[]), \
|
||||
patch("subprocess.run") as mock_run:
|
||||
# Mock subprocess to return empty
|
||||
mock_run.return_value.returncode = 0
|
||||
mock_run.return_value.stdout = ""
|
||||
|
||||
# Clear cache first
|
||||
service.clear_port_scan_cache()
|
||||
|
||||
# First scan should invoke list_ports and subprocess
|
||||
ports1 = service.enumerate_ports()
|
||||
assert mock_run.call_count == 1
|
||||
|
||||
# Second scan within 2 seconds should return cached list without invoking subprocess
|
||||
ports2 = service.enumerate_ports()
|
||||
assert mock_run.call_count == 1
|
||||
assert ports1 == ports2
|
||||
|
||||
# Clear cache and scan again should invoke subprocess
|
||||
service.clear_port_scan_cache()
|
||||
ports3 = service.enumerate_ports()
|
||||
assert mock_run.call_count == 2
|
||||
|
||||
|
||||
Reference in New Issue
Block a user