"""Проверки тонкого порта ВЭП на настоящей C DLL templates.""" # Транспорт и Qt здесь не участвуют. import pytest # Параметризует все физические адреса и неверные аргументы. from protocan.uksvep import UksvepCan # Проверяет общий production-порт без отдельного Python codec. @pytest.fixture(scope="module") # Все сценарии используют одну загруженную нативную библиотеку. def core(): # Отсутствие C-профиля должно приводить к ошибке теста, а не fallback. return UksvepCan() # Привязывает те же экспорты, что панель SETGUI. @pytest.mark.parametrize("device", range(16)) # Четыре перемычки задают все шестнадцать адресов. def test_routes_and_mode(core, device): # Разделяет DeviceID, Mode и legacy CAN-пару. assert core.can_id(device) == 0x001CE020 + device # Команды идут по маршруту Setup_CAN_addr. assert core.can_id(device, True) == 0x001CE000 + device # Данные устройства не содержат командное смещение. fields = {item.address: item for item in core.fields(device)} # Карта адресов приходит только из C. assert fields[126].initial == device + 1 and fields[100].maximum == 10 # Mode и десятиотсчётная яркость совпадают с прошивкой. @pytest.mark.parametrize("device,address,values", [(16, 0, (1,)), (-1, 0, (1,)), (0, -1, (1,)), (0, 128, (1,)), (0, 127, (1, 2)), (0, 0, ()), (0, 0, (-1,)), (0, 0, (65536,))]) # Все ошибки должны обнаруживаться до ctypes-обрезания. def test_invalid_arguments(core, device, address, values): # Неверный ввод не адресует соседнюю плату или слово. with pytest.raises(ValueError): # Требует явный отказ вместо скрытого modulo. core.frames(device, address, values) # Использует реальный путь формирования GUI-снимка. def test_golden_and_sparse_rx(core): # Эталон независимо проверяет endian, маску и пропущенное слово. frames = core.frames(5, 8, (0x1234, 0xABCD, 0x8001)) # Значения различаются во всех значащих позициях. assert [(frame.can_id, frame.data) for frame in frames] == [(0x001CE025, bytes.fromhex("AB CD 80 01 E0 08 12 34"))] # Фиксирует точный wire-vector STM32-проекта. packet = core.decode(5, 0x001CE005, bytes.fromhex("AA 55 33 44 A0 7D 11 22")) # Маска101 разрешает R125 и R127. assert packet.from_device and packet.values == ((125, 0x1122), (127, 0x3344)) # Отсутствующий R126 не затирается чужим значением. assert core.decode(5, 0xBA0018, frames[0].data) is None # Другой ВЭП Бальзам не смешивается с UKSVEP. with pytest.raises(ValueError): # Повреждённый собственный пакет должен считаться ошибкой RX. core.decode(5, 0x001CE005, frames[0].data[:7]) # DLC7 недостаточен для исторического payload. def test_fields_inputs_and_special_polarity(core): # Карта и расчёт не должны копировать семантику задатчика. fields = {item.address: item for item in core.fields()} # Все defaults взяты из C-профиля package.h. assert set(fields) == {0, 1, 2, 8, 9, 23, 48, 100, 101, 102, 125, 126, 127} # Не добавляет несуществующие аналоговые измерения. assert fields[8].initial == 0x277F and fields[9].initial == 0x200 # Маски повторяют Default_params. assert "PB12" in core.names("inputs")[9] and "активный 1" in core.names("inputs")[9] # Единственный прямой вход имеет особую полярность. assert core.names("commands")[0] == "Тест ламп" and not core.names("commands")[4] # Команды задатчика и резервные биты не подставляются. assert core.evaluate(0x247F, 0x277F, 0x200) == (0, 0) # Исправное состояние не вызывает ложных аварий. assert core.evaluate(0x27FF, 0, 0) == (0x300, 0x200) # Разряд и 24 В сохраняют особую семантику без масок; заряд не влияет.