""" Shared fixtures for laser_control tests. """ import pytest import struct from unittest.mock import MagicMock, patch from laser_control.protocol import Protocol, _build_crc, _flipfour, _int_to_hex4 from laser_control.controller import LaserController from laser_control.conversions import ( current_n_to_ma, temp_n_to_c, temp_ext_n_to_c, voltage_3v3_n_to_v, voltage_5v_n_to_v, voltage_7v_n_to_v, ) def make_valid_response( current1_n: int = 10000, current2_n: int = 12000, temp1_n: int = 30000, temp2_n: int = 32000, temp_ext1_n: int = 2048, temp_ext2_n: int = 2048, mon_3v3_n: int = 2703, # ~3.3V mon_5v1_n: int = 2731, # ~5.0V mon_5v2_n: int = 2731, mon_7v0_n: int = 1042, # ~7.0V message_id: int = 12345, ) -> bytes: """ Build a syntactically valid 30-byte DATA response. Words (each 2 bytes, little-endian via flipfour): 0 header 1 I1 2 I2 3 TO6_LSB 4 TO6_MSB 5 Temp_1 6 Temp_2 7 Temp_Ext_1 8 Temp_Ext_2 9 MON_3V3 10 MON_5V1 11 MON_5V2 12 MON_7V0 13 Message_ID 14 CRC """ words_raw = [ 0xABCD, # Word 0 header current1_n, # Word 1 current2_n, # Word 2 0, # Word 3 TO6_LSB 0, # Word 4 TO6_MSB temp1_n, # Word 5 temp2_n, # Word 6 temp_ext1_n, # Word 7 temp_ext2_n, # Word 8 mon_3v3_n, # Word 9 mon_5v1_n, # Word 10 mon_5v2_n, # Word 11 mon_7v0_n, # Word 12 message_id, # Word 13 0, # Word 14 CRC placeholder ] # Build hex string with flipfour applied hex_str = "" for w in words_raw: hex_str += _flipfour(_int_to_hex4(w)) # Compute CRC over words 1..13 (indices 4..55 in hex, i.e. skip word 0) words_hex = [hex_str[i:i+4] for i in range(0, len(hex_str), 4)] crc_words = words_hex[1:14] # words 1..13 crc_val = int(crc_words[0], 16) for w in crc_words[1:]: crc_val ^= int(w, 16) # Replace CRC word hex_str = hex_str[:56] + _flipfour(_int_to_hex4(crc_val)) return bytes.fromhex(hex_str) @pytest.fixture def valid_response_bytes(): """Pre-built valid 30-byte device response.""" return make_valid_response() @pytest.fixture def mock_serial(): """Mock serial.Serial object.""" with patch('serial.Serial') as mock_cls: mock_instance = MagicMock() mock_instance.is_open = True mock_cls.return_value = mock_instance yield mock_instance @pytest.fixture def connected_controller(mock_serial): """LaserController with mocked serial connection.""" mock_serial.read.return_value = make_valid_response() ctrl = LaserController(port='/dev/ttyUSB0') with patch('serial.Serial', return_value=mock_serial): ctrl._protocol._serial = mock_serial mock_serial.is_open = True return ctrl