def test_check_for_error_response_returns_none_on_normal_response(self):
        serial_packet = module.SerialPacket(
            command=0x20,
            data_bytes_count=0x00,
            data_bytes=b"",
            **PREFIX_AND_ADDR_DEFAULTS,
        )

        assert module._check_for_error_response(serial_packet) is None
    def test_check_for_error_response_identifies_echoed_command(self):
        serial_packet = module.SerialPacket(
            command=0x0F,
            data_bytes_count=0x02,
            data_bytes=b"\x03\x99",
            **PREFIX_AND_ADDR_DEFAULTS,
        )

        with pytest.raises(module.ErrorResponse) as e:
            module._check_for_error_response(serial_packet)

        assert f"0x{0x99:02X}" in str(e.value)
    def test_check_for_error_response_identifies_bad_command_error_type(self):
        serial_packet = module.SerialPacket(
            command=0x0F,
            data_bytes_count=0x02,
            data_bytes=b"\x01\x99",
            **PREFIX_AND_ADDR_DEFAULTS,
        )

        with pytest.raises(module.ErrorResponse) as e:
            module._check_for_error_response(serial_packet)

        assert "Bad Command" in str(e.value)
    def test_repr(self):
        packet = module.SerialPacket(
            prefix=0xCA,
            device_address_msb=0x00,
            device_address_lsb=0x01,
            command=0x20,
            data_bytes_count=0x03,
            data_bytes=b"\x11\x02\x71",
            checksum=0x57,
        )

        assert "0xCA 0x00 0x01 0x20 0x03 0x11 0x02 0x71 0x57" in repr(packet)
    def test_returns_response_serial_packet_from_bytes(
            self, mock_serial_and_response):
        mock_command_packet = Mock()
        mock_serial_and_response.return_value = b"\xCA\x00\x01\x20\x03\x11\x02\x71\x57"

        actual = module.send_command(sentinel.port, mock_command_packet)

        expected_response_packet = module.SerialPacket(
            command=0x20,
            data_bytes_count=0x03,
            data_bytes=b"\x11\x02\x71",
            **PREFIX_AND_ADDR_DEFAULTS,
        )

        assert actual == expected_response_packet
class TestSerialPacket:
    @pytest.mark.parametrize(
        "name, packet_bytes, expected_packet",
        [
            (
                "packet with data bytes",
                b"\xCA\x00\x01\x20\x03\x11\x02\x71\x57",
                module.SerialPacket(
                    prefix=0xCA,
                    device_address_msb=0x00,
                    device_address_lsb=0x01,
                    command=0x20,
                    data_bytes_count=0x03,
                    data_bytes=b"\x11\x02\x71",
                    checksum=0x57,
                ),
            ),
            (
                "packet with no data bytes",
                b"\xCA\x00\x01\x20\x00\xDE",
                module.SerialPacket(
                    prefix=0xCA,
                    device_address_msb=0x00,
                    device_address_lsb=0x01,
                    command=0x20,
                    data_bytes_count=0x00,
                    data_bytes=b"",
                    checksum=0xDE,
                ),
            ),
        ],
    )
    def test_init_from_bytes(self, name, packet_bytes, expected_packet):
        actual_packet = module.SerialPacket.from_bytes(packet_bytes)
        assert actual_packet == expected_packet

    @pytest.mark.parametrize(
        "name, response_bytes",
        [
            ("incorrect prefix", b"\xAA\x00\x01\x20\x04\x11\x02\x71\x57"),
            ("incorrect address msb", b"\xCA\x01\x01\x20\x04\x11\x02\x71\x57"),
            ("incorrect address lsb", b"\xCA\x00\x99\x20\x04\x11\x02\x71\x57"),
            ("data byte count mismatch",
             b"\xCA\x00\x01\x20\x04\x11\x02\x71\x57"),
            ("incorrect checksum", b"\xCA\x00\x01\x20\x03\x11\x02\x71\x58"),
        ],
    )
    def test_init_from_bytes_raises_if_invalid(self, name, response_bytes):
        with pytest.raises(ValueError):
            module.SerialPacket.from_bytes(response_bytes)

    @pytest.mark.parametrize(
        "name, command, data_bytes, expected_packet",
        [
            (
                "command with data bytes",
                0x20,
                b"\x11\x02\x71",
                module.SerialPacket(
                    prefix=0xCA,
                    device_address_msb=0x00,
                    device_address_lsb=0x01,
                    command=0x20,
                    data_bytes_count=0x03,
                    data_bytes=b"\x11\x02\x71",
                    checksum=0x57,
                ),
            ),
            (
                "command with no data bytes",
                0x20,
                b"",
                module.SerialPacket(
                    prefix=0xCA,
                    device_address_msb=0x00,
                    device_address_lsb=0x01,
                    command=0x20,
                    data_bytes_count=0x00,
                    data_bytes=b"",
                    checksum=0xDE,
                ),
            ),
        ],
    )
    def test_init_from_command(self, name, command, data_bytes,
                               expected_packet):
        actual_packet = module.SerialPacket.from_command(command, data_bytes)
        assert actual_packet == expected_packet

    @pytest.mark.parametrize(
        "packet_bytes",
        [
            b"\xCA\x00\x01\x20\x03\x11\x02\x71\x57",
            b"\xCA\x00\x01\x20\x00\xDE",
            b"\xCA\x00\x01\x00\x00\xFE",
            b"\xCA\x00\x01\x09\x00\xF5",
            b"\xCA\x00\x01\x20\x03\x11\x02\x71\x57",
            b"\xCA\x00\x01\xF0\x02\x01\x2C\xDF",
            b"\xCA\x00\x01\xF0\x03\x11\x01\x2C\xCD",
        ],
    )
    def test_init_from_bytes_round_trip_to_bytes(self, packet_bytes):
        packet = module.SerialPacket.from_bytes(packet_bytes)
        # hexlify to make error message more readable
        assert hexlify(packet.to_bytes()) == hexlify(packet_bytes)

    def test_str(self):
        packet = module.SerialPacket(
            prefix=0xCA,
            device_address_msb=0x00,
            device_address_lsb=0x01,
            command=0x20,
            data_bytes_count=0x03,
            data_bytes=b"\x11\x02\x71",
            checksum=0x57,
        )

        assert "0xCA 0x00 0x01 0x20 0x03 0x11 0x02 0x71 0x57" in str(packet)

    def test_repr(self):
        packet = module.SerialPacket(
            prefix=0xCA,
            device_address_msb=0x00,
            device_address_lsb=0x01,
            command=0x20,
            data_bytes_count=0x03,
            data_bytes=b"\x11\x02\x71",
            checksum=0x57,
        )

        assert "0xCA 0x00 0x01 0x20 0x03 0x11 0x02 0x71 0x57" in repr(packet)