Files
py-dvt-ate/tests/unit/test_scpi_parser.py
Kai Chappell fb7945fc84 Add SCPI parser tests
Comprehensive test suite for SCPI command parsing:
- SCPICommand dataclass tests (creation, keyword property)
- Parser tests for queries, commands, arguments
- IEEE 488.2 common command tests (*IDN?, *RST, etc.)
- Edge cases (whitespace, empty strings)
- Instrument-specific command tests

Also fixed bug where is_query was determined from command string
ending rather than header ending (handles queries with arguments).
2025-12-02 12:39:00 +00:00

204 lines
6.8 KiB
Python

"""Unit tests for SCPI command parsing."""
import pytest
from py_dvt_ate.instruments.scpi import SCPICommand, SCPIParser
class TestSCPICommand:
"""Tests for the SCPICommand dataclass."""
def test_creation(self) -> None:
"""Test SCPICommand can be created with valid values."""
cmd = SCPICommand(
header="VOLT",
arguments=["3.3"],
is_query=False,
)
assert cmd.header == "VOLT"
assert cmd.arguments == ["3.3"]
assert cmd.is_query is False
def test_keyword_for_command(self) -> None:
"""Test keyword property for regular command."""
cmd = SCPICommand(header="VOLT", arguments=["3.3"], is_query=False)
assert cmd.keyword == "VOLT"
def test_keyword_for_query(self) -> None:
"""Test keyword property strips '?' from query."""
cmd = SCPICommand(header="VOLT?", arguments=[], is_query=True)
assert cmd.keyword == "VOLT"
def test_keyword_for_nested_command(self) -> None:
"""Test keyword property for nested SCPI command."""
cmd = SCPICommand(header="TEMP:SETPOINT?", arguments=[], is_query=True)
assert cmd.keyword == "TEMP:SETPOINT"
class TestSCPIParser:
"""Tests for the SCPIParser class."""
@pytest.fixture
def parser(self) -> SCPIParser:
"""Create parser instance for tests."""
return SCPIParser()
def test_parse_simple_query(self, parser: SCPIParser) -> None:
"""Test parsing simple query command."""
cmd = parser.parse("*IDN?")
assert cmd.header == "*IDN?"
assert cmd.arguments == []
assert cmd.is_query is True
assert cmd.keyword == "*IDN"
def test_parse_simple_command(self, parser: SCPIParser) -> None:
"""Test parsing simple command without arguments."""
cmd = parser.parse("*RST")
assert cmd.header == "*RST"
assert cmd.arguments == []
assert cmd.is_query is False
assert cmd.keyword == "*RST"
def test_parse_command_with_single_argument(self, parser: SCPIParser) -> None:
"""Test parsing command with single numeric argument."""
cmd = parser.parse("VOLT 3.3")
assert cmd.header == "VOLT"
assert cmd.arguments == ["3.3"]
assert cmd.is_query is False
def test_parse_command_with_multiple_arguments(self, parser: SCPIParser) -> None:
"""Test parsing command with comma-separated arguments."""
cmd = parser.parse("CONF:VOLT:DC 10,0.001")
assert cmd.header == "CONF:VOLT:DC"
assert cmd.arguments == ["10", "0.001"]
assert cmd.is_query is False
def test_parse_nested_scpi_command(self, parser: SCPIParser) -> None:
"""Test parsing nested SCPI command hierarchy."""
cmd = parser.parse("TEMP:SETPOINT 85.0")
assert cmd.header == "TEMP:SETPOINT"
assert cmd.arguments == ["85.0"]
assert cmd.is_query is False
assert cmd.keyword == "TEMP:SETPOINT"
def test_parse_nested_scpi_query(self, parser: SCPIParser) -> None:
"""Test parsing nested SCPI query."""
cmd = parser.parse("TEMP:SETPOINT?")
assert cmd.header == "TEMP:SETPOINT?"
assert cmd.arguments == []
assert cmd.is_query is True
def test_parse_ieee_common_commands(self, parser: SCPIParser) -> None:
"""Test parsing IEEE 488.2 common commands."""
# Identity query
cmd = parser.parse("*IDN?")
assert cmd.is_query is True
assert cmd.keyword == "*IDN"
# Reset
cmd = parser.parse("*RST")
assert cmd.is_query is False
assert cmd.keyword == "*RST"
# Clear status
cmd = parser.parse("*CLS")
assert cmd.is_query is False
assert cmd.keyword == "*CLS"
# Operation complete query
cmd = parser.parse("*OPC?")
assert cmd.is_query is True
assert cmd.keyword == "*OPC"
def test_parse_strips_whitespace(self, parser: SCPIParser) -> None:
"""Test parser strips leading and trailing whitespace."""
cmd = parser.parse(" VOLT 3.3 ")
assert cmd.header == "VOLT"
assert cmd.arguments == ["3.3"]
def test_parse_strips_argument_whitespace(self, parser: SCPIParser) -> None:
"""Test parser strips whitespace from arguments."""
cmd = parser.parse("CONF:VOLT:DC 10 , 0.001 ")
assert cmd.arguments == ["10", "0.001"]
def test_parse_empty_string(self, parser: SCPIParser) -> None:
"""Test parsing empty string returns empty command."""
cmd = parser.parse("")
assert cmd.header == ""
assert cmd.arguments == []
assert cmd.is_query is False
def test_parse_whitespace_only(self, parser: SCPIParser) -> None:
"""Test parsing whitespace-only string returns empty command."""
cmd = parser.parse(" ")
assert cmd.header == ""
assert cmd.arguments == []
assert cmd.is_query is False
def test_parse_output_on_off(self, parser: SCPIParser) -> None:
"""Test parsing output enable/disable commands."""
cmd_on = parser.parse("OUTP ON")
assert cmd_on.arguments == ["ON"]
cmd_off = parser.parse("OUTP OFF")
assert cmd_off.arguments == ["OFF"]
cmd_1 = parser.parse("OUTP 1")
assert cmd_1.arguments == ["1"]
cmd_0 = parser.parse("OUTP 0")
assert cmd_0.arguments == ["0"]
def test_parse_channel_select(self, parser: SCPIParser) -> None:
"""Test parsing channel selection commands."""
cmd = parser.parse("INST:SEL CH1")
assert cmd.header == "INST:SEL"
assert cmd.arguments == ["CH1"]
def test_parse_measurement_query(self, parser: SCPIParser) -> None:
"""Test parsing measurement query commands."""
cmd = parser.parse("MEAS:VOLT:DC?")
assert cmd.header == "MEAS:VOLT:DC?"
assert cmd.is_query is True
assert cmd.keyword == "MEAS:VOLT:DC"
def test_parse_measurement_with_range(self, parser: SCPIParser) -> None:
"""Test parsing measurement query with range argument."""
cmd = parser.parse("MEAS:VOLT:DC? AUTO")
assert cmd.header == "MEAS:VOLT:DC?"
assert cmd.arguments == ["AUTO"]
assert cmd.is_query is True
def test_parse_system_error_query(self, parser: SCPIParser) -> None:
"""Test parsing system error query."""
cmd = parser.parse("SYST:ERR?")
assert cmd.header == "SYST:ERR?"
assert cmd.is_query is True
assert cmd.keyword == "SYST:ERR"
def test_parse_nplc_setting(self, parser: SCPIParser) -> None:
"""Test parsing NPLC (integration time) command."""
cmd = parser.parse("SENS:VOLT:DC:NPLC 10")
assert cmd.header == "SENS:VOLT:DC:NPLC"
assert cmd.arguments == ["10"]
assert cmd.is_query is False