""" Tests for communication protocol module. Testing command encoding/decoding, CRC calculations, and protocol message structure. """ import pytest from unittest.mock import Mock, MagicMock, patch, call import struct from laser_control.protocol import ( Protocol, CommandCode, TaskType, Message, Response ) from laser_control.exceptions import ( CommunicationError, CRCError, ProtocolError ) class TestCRCCalculation: """Test CRC calculation and verification.""" def test_crc_calculation(self): """Test CRC calculation for known data (at least 2 words needed).""" # calculate_crc skips word 0 and XORs words 1..N # So we need at least 4 bytes (2 words) data = b'\x00\x01\x02\x03\x04\x05\x06\x07' crc = Protocol.calculate_crc(data) assert isinstance(crc, int) assert 0 <= crc <= 0xFFFF def test_crc_consistency(self): """Test CRC calculation consistency.""" data = b'\x11\x11' + b'\x00' * 26 + b'\xFF\xFF' # 30 bytes crc1 = Protocol.calculate_crc(data) crc2 = Protocol.calculate_crc(data) assert crc1 == crc2 def test_crc_different_data(self): """Test CRC differs for different data.""" data1 = b'\x00\x00\x01\x02\x03\x04' data2 = b'\x00\x00\x05\x06\x07\x08' crc1 = Protocol.calculate_crc(data1) crc2 = Protocol.calculate_crc(data2) assert crc1 != crc2 class TestMessageEncoding: """Test message encoding for device commands.""" def test_encode_decode_enable_command(self): """Test encoding DECODE_ENABLE command.""" message = Protocol.encode_decode_enable( temp1=25.5, temp2=30.0, current1=40.0, current2=35.0, pi_coeff1_p=1, pi_coeff1_i=1, pi_coeff2_p=1, pi_coeff2_i=1, message_id=12345 ) assert isinstance(message, bytes) assert len(message) == 30 # Expected message length # Check command code (0x1111 stored little-endian via flipfour → 0x11 0x11) assert message[0] == 0x11 assert message[1] == 0x11 def test_encode_task_enable_command(self): """Test encoding TASK_ENABLE command.""" message = Protocol.encode_task_enable( task_type=TaskType.CHANGE_CURRENT_LD1, static_temp1=25.0, static_temp2=30.0, static_current1=40.0, static_current2=35.0, min_value=20.0, max_value=50.0, step=0.5, time_step=50, delay_time=5, message_id=54321 ) assert isinstance(message, bytes) assert len(message) > 0 # Check command code command = struct.unpack(' 0xFFFF should wrap (& 0xFFFF in controller) large_id = 0x10000 + 123 wrapped = large_id & 0xFFFF message = Protocol.encode_decode_enable( temp1=25.0, temp2=30.0, current1=40.0, current2=35.0, pi_coeff1_p=1, pi_coeff1_i=1, pi_coeff2_p=1, pi_coeff2_i=1, message_id=wrapped, ) assert isinstance(message, bytes) assert len(message) == 30 def test_negative_values_handling(self): """Test handling of negative values where not allowed.""" with pytest.raises(ValueError): Protocol.encode_decode_enable( temp1=25.0, temp2=30.0, current1=-10.0, # Negative current current2=35.0, pi_coeff1_p=1.0, pi_coeff1_i=0.5, pi_coeff2_p=1.0, pi_coeff2_i=0.5, message_id=12345 )