Files
templates/python/tests/test_byte_work_panel.py

214 lines
8.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""The reusable terminal must work without a Tms2812Tab or its private state."""
import os
from pathlib import Path
import subprocess
import sys
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
import pytest
from PySide6.QtTest import QTest
from PySide6.QtWidgets import QApplication, QTabWidget
from pysetterminal.panel import ByteWorkPanel, build_byte_page
class Protocol:
def __init__(self):
self.requests = []
self.callback = None
self.cancelled = 0
def request_async(self, address, value, controller, callback):
self.requests.append((address, value, controller))
self.callback = callback
def reply(self, success, result):
callback, self.callback = self.callback, None
callback(success, result)
def cancel_async(self):
self.cancelled += 1
if self.callback is not None:
self.reply(False, "cancelled")
def poke(self, address, value, controller):
self.requests.append((address, value, controller))
return True, {"raw": b"ack"}
def peek(self, address, controller):
self.requests.append((address, None, controller))
return True, {"value": 0x1234, "raw_response": {"raw": b"reply"}}
def send_raw(self, text):
return True, text
def receive_raw(self):
return False, "Нет данных"
@pytest.fixture(scope="module")
def app():
return QApplication.instance() or QApplication([])
@pytest.fixture
def page(app, tmp_path):
protocol = Protocol()
widget = ByteWorkPanel(protocol, device_address=7, macros_file=tmp_path / "macros.json")
yield widget, protocol
widget.close()
app.processEvents()
def test_shared_panel_imports_and_constructs_without_desktop_application(tmp_path):
templates = Path(__file__).resolve().parents[1]
code = """
import importlib.abc, sys
class BlockDesktop(importlib.abc.MetaPathFinder):
def find_spec(self, fullname, path=None, target=None):
if fullname.split('.')[0] == 'gui_desktop':
raise ImportError('Unexpected desktop dependency')
sys.meta_path.insert(0, BlockDesktop())
from PySide6.QtWidgets import QApplication, QTabWidget
from pysetterminal.panel import build_byte_page
from pysetterminal.rawprotocol import RawProtocol
app = QApplication([])
tabs = QTabWidget()
page = build_byte_page(RawProtocol(), macros_file='macros.json')
tabs.addTab(page, 'Работа с байтом')
assert tabs.widget(0) is page
assert 'gui_desktop' not in sys.modules
assert page.bus_widget and page.raw_widget
page.close()
"""
result = subprocess.run([sys.executable, "-c", code], cwd=tmp_path,
env=dict(os.environ, PYTHONPATH=str(templates)),
capture_output=True, text=True, timeout=30)
assert result.returncode == 0, result.stderr
def test_multiple_tabs_keep_controls_status_and_macros_separate(app, tmp_path):
calls = []
tabs = QTabWidget()
first = build_byte_page(Protocol(), start_word=lambda *args: calls.append(args),
macros_file=tmp_path / "first.json")
second = ByteWorkPanel(Protocol(), macros_file=tmp_path / "second.json")
tabs.addTab(first, "Первый")
tabs.addTab(second, "Второй")
first.byte_write_value.setText("0301")
first.byte_write_value.textEdited.emit("0301")
first.byte_write_address.setText("0x1234")
first.byte_write_button.click()
first.set_read_value(0x1234, 0x0301)
first.set_status("Готово")
first.bus_widget.macros = [{"name": "Первый", "commands": []}]
first.bus_widget.save_macros()
assert calls == [(True, 0x1234, 0x0301)]
assert first.byte_write_binary.text() == "0000 0011 0000 0001"
assert second.byte_write_value.text() == "0000"
assert second.byte_read_value.text() == ""
assert second.status.text() == "Нет данных от контроллера"
second.bus_widget.load_macros()
assert second.bus_widget.macros == []
first.close()
second.close()
tabs.close()
@pytest.mark.parametrize("address", ["4096", "$1000", "0x1000"])
def test_read_accepts_address_formats_and_displays_both_values(page, address):
widget, protocol = page
widget.byte_read_address.setText(address)
widget.byte_read_button.click()
assert protocol.requests == [(0x1000, None, 7)]
assert not widget.byte_read_button.isEnabled()
protocol.reply(True, {"value": 0xABCD})
assert widget.byte_read_address.text() == "$1000"
assert widget.byte_read_value.text() == "ABCD"
assert widget.byte_read_binary.text() == "1010 1011 1100 1101"
assert widget.byte_read_button.isEnabled()
@pytest.mark.parametrize("address,value,binary", [
("-1", "0000", "0"),
("$100000000", "0000", "0"),
("invalid", "0000", "0"),
("$1000", "10000", "10000000000000000"),
("$1000", "0001", "0"),
("$1000", "GGGG", "0"),
("$1000", "0000", "invalid"),
])
def test_invalid_inputs_do_not_send(page, address, value, binary):
widget, protocol = page
widget.byte_write_address.setText(address)
widget.byte_write_value.setText(value)
widget.byte_write_binary.setText(binary)
widget.byte_write_button.click()
assert protocol.requests == []
assert "Проверьте" in widget.status.text()
@pytest.mark.parametrize("readback,confirmed", [(0x0301, True), (0x0302, False)])
def test_write_is_verified_and_controller_is_captured_for_whole_operation(page, readback, confirmed):
widget, protocol = page
controller = [13]
widget.device_address = lambda: controller[0]
widget.byte_write_value.setText("0301")
widget.sync_byte_value("0301", 16)
widget.byte_write_button.click()
assert protocol.requests == [(0x1000, 0x0301, 13)]
controller[0] = 21
protocol.reply(True, {"raw": b"ack"})
assert protocol.requests[-1] == (0x1000, None, 13)
protocol.reply(True, {"value": readback})
assert widget.byte_read_value.text() == f"{readback:04X}"
assert ("Запись проверена" in widget.status.text()) == confirmed
if not confirmed:
assert "не подтверждена" in widget.status.text()
assert widget.byte_write_button.isEnabled()
def test_failure_and_cancellation_restore_controls_and_ignore_late_reply(page):
widget, protocol = page
widget.byte_read_button.click()
protocol.reply(False, "CRC ответа не совпадает")
assert widget.status.text() == "CRC ответа не совпадает"
assert widget.byte_read_button.isEnabled()
widget.byte_read_button.click()
late_reply = protocol.callback
widget.stop()
assert protocol.cancelled == 1
widget.byte_read_button.click()
late_reply(True, {"value": 0x9999})
assert widget.byte_read_value.text() == ""
assert not widget.byte_read_button.isEnabled()
protocol.reply(True, {"value": 0x1234})
assert widget.byte_read_value.text() == "1234"
assert widget.byte_read_button.isEnabled()
def test_controller_provider_is_used_by_bus_raw_memory_and_macros(app, tmp_path):
protocol = Protocol()
controller = [21]
widget = ByteWorkPanel(protocol, device_address=lambda: controller[0],
macros_file=tmp_path / "address.json")
try:
widget.raw_widget.memory_write()
widget.raw_widget.memory_read()
assert protocol.requests == [(0x2000, 0, 21), (0x2000, None, 21)]
widget.bus_widget.read_from_bus()
assert protocol.requests[-2:] == [(0x200B, 0, 21), (0x200F, None, 21)]
widget.bus_widget.execute_macro_commands([{"type": "read", "tetrad": 3, "register": 5}])
controller[0] = 22
QTest.qWait(20)
assert protocol.requests[-1] == (0x200B, 0x35, 21)
protocol.reply(True, {"raw": b"ack"})
QTest.qWait(20)
assert protocol.requests[-1] == (0x200F, None, 21)
protocol.reply(True, {"value": 0x4321, "raw_response": {"raw": b"reply"}})
QTest.qWait(20)
assert not widget.bus_widget._macro_running
assert widget.bus_widget.bus_read_value_label.text() == "0x4321 (17185)"
finally:
widget.close()