- Use X | None syntax instead of Optional[X] (UP045) - Sort imports in dashboard app (I001) - Remove unnecessary UTF-8 encoding argument (UP012) - Add 'from err' to exception re-raises (B904) - Remove unused imports in integration tests (F401) - Fix useless expression in test (B018) - Cast **1.5 result to float in LDO model (mypy no-any-return) - Use functools.partial instead of lambda in server (mypy misc)
215 lines
6.5 KiB
Python
215 lines
6.5 KiB
Python
"""Unit tests for physics state dataclasses and engine stub."""
|
|
|
|
import pytest
|
|
|
|
from py_dvt_ate.simulation.physics.engine import PhysicsEngine
|
|
from py_dvt_ate.simulation.physics.state import ElectricalState, ThermalState
|
|
|
|
|
|
class TestThermalState:
|
|
"""Tests for the ThermalState dataclass."""
|
|
|
|
def test_creation(self) -> None:
|
|
"""Test ThermalState can be created with valid values."""
|
|
state = ThermalState(
|
|
chamber_temperature=25.0,
|
|
case_temperature=30.0,
|
|
junction_temperature=35.0,
|
|
timestamp=0.0,
|
|
)
|
|
|
|
assert state.chamber_temperature == 25.0
|
|
assert state.case_temperature == 30.0
|
|
assert state.junction_temperature == 35.0
|
|
assert state.timestamp == 0.0
|
|
|
|
def test_immutability(self) -> None:
|
|
"""Test ThermalState is immutable (frozen dataclass)."""
|
|
state = ThermalState(
|
|
chamber_temperature=25.0,
|
|
case_temperature=30.0,
|
|
junction_temperature=35.0,
|
|
timestamp=0.0,
|
|
)
|
|
|
|
with pytest.raises(AttributeError):
|
|
state.chamber_temperature = 50.0 # type: ignore[misc]
|
|
|
|
def test_equality(self) -> None:
|
|
"""Test ThermalState equality comparison."""
|
|
state1 = ThermalState(
|
|
chamber_temperature=25.0,
|
|
case_temperature=30.0,
|
|
junction_temperature=35.0,
|
|
timestamp=0.0,
|
|
)
|
|
state2 = ThermalState(
|
|
chamber_temperature=25.0,
|
|
case_temperature=30.0,
|
|
junction_temperature=35.0,
|
|
timestamp=0.0,
|
|
)
|
|
|
|
assert state1 == state2
|
|
|
|
def test_hashable(self) -> None:
|
|
"""Test ThermalState is hashable (for use in sets/dicts)."""
|
|
state = ThermalState(
|
|
chamber_temperature=25.0,
|
|
case_temperature=30.0,
|
|
junction_temperature=35.0,
|
|
timestamp=0.0,
|
|
)
|
|
|
|
# Should not raise
|
|
hash(state)
|
|
_ = {state} # Can be added to a set
|
|
|
|
|
|
class TestElectricalState:
|
|
"""Tests for the ElectricalState dataclass."""
|
|
|
|
def test_creation(self) -> None:
|
|
"""Test ElectricalState can be created with valid values."""
|
|
state = ElectricalState(
|
|
input_voltage=5.0,
|
|
output_voltage=3.3,
|
|
load_current=0.1,
|
|
quiescent_current=50e-6,
|
|
power_dissipation=0.17,
|
|
)
|
|
|
|
assert state.input_voltage == 5.0
|
|
assert state.output_voltage == 3.3
|
|
assert state.load_current == 0.1
|
|
assert state.quiescent_current == 50e-6
|
|
assert state.power_dissipation == 0.17
|
|
|
|
def test_immutability(self) -> None:
|
|
"""Test ElectricalState is immutable (frozen dataclass)."""
|
|
state = ElectricalState(
|
|
input_voltage=5.0,
|
|
output_voltage=3.3,
|
|
load_current=0.1,
|
|
quiescent_current=50e-6,
|
|
power_dissipation=0.17,
|
|
)
|
|
|
|
with pytest.raises(AttributeError):
|
|
state.output_voltage = 1.8 # type: ignore[misc]
|
|
|
|
def test_equality(self) -> None:
|
|
"""Test ElectricalState equality comparison."""
|
|
state1 = ElectricalState(
|
|
input_voltage=5.0,
|
|
output_voltage=3.3,
|
|
load_current=0.1,
|
|
quiescent_current=50e-6,
|
|
power_dissipation=0.17,
|
|
)
|
|
state2 = ElectricalState(
|
|
input_voltage=5.0,
|
|
output_voltage=3.3,
|
|
load_current=0.1,
|
|
quiescent_current=50e-6,
|
|
power_dissipation=0.17,
|
|
)
|
|
|
|
assert state1 == state2
|
|
|
|
|
|
class TestPhysicsEngineStub:
|
|
"""Tests for the PhysicsEngine stub."""
|
|
|
|
def test_creation_default(self) -> None:
|
|
"""Test PhysicsEngine can be created with defaults."""
|
|
engine = PhysicsEngine()
|
|
|
|
assert engine.dt == pytest.approx(0.01) # 100Hz -> 10ms
|
|
|
|
def test_creation_custom_rate(self) -> None:
|
|
"""Test PhysicsEngine with custom update rate."""
|
|
engine = PhysicsEngine(update_rate_hz=50.0)
|
|
|
|
assert engine.dt == pytest.approx(0.02) # 50Hz -> 20ms
|
|
|
|
def test_step_advances_time(self) -> None:
|
|
"""Test step() advances simulation time."""
|
|
engine = PhysicsEngine(update_rate_hz=100.0)
|
|
|
|
assert engine.simulation_time == pytest.approx(0.0)
|
|
engine.step()
|
|
assert engine.simulation_time == pytest.approx(0.01)
|
|
engine.step()
|
|
assert engine.simulation_time == pytest.approx(0.02)
|
|
|
|
def test_get_thermal_state(self) -> None:
|
|
"""Test get_thermal_state returns ThermalState."""
|
|
engine = PhysicsEngine()
|
|
|
|
state = engine.get_thermal_state()
|
|
|
|
assert isinstance(state, ThermalState)
|
|
assert state.chamber_temperature == 25.0 # Default
|
|
assert state.timestamp == 0.0
|
|
|
|
def test_get_electrical_state(self) -> None:
|
|
"""Test get_electrical_state returns ElectricalState."""
|
|
engine = PhysicsEngine()
|
|
|
|
state = engine.get_electrical_state()
|
|
|
|
assert isinstance(state, ElectricalState)
|
|
|
|
def test_set_chamber_setpoint(self) -> None:
|
|
"""Test set_chamber_setpoint updates setpoint."""
|
|
engine = PhysicsEngine()
|
|
|
|
engine.set_chamber_setpoint(85.0)
|
|
|
|
# Stub doesn't update chamber temp, just stores setpoint
|
|
state = engine.get_thermal_state()
|
|
assert state.chamber_temperature == 25.0 # Still at initial
|
|
|
|
def test_set_input_voltage(self) -> None:
|
|
"""Test set_input_voltage updates voltage."""
|
|
engine = PhysicsEngine()
|
|
|
|
engine.set_input_voltage(5.0)
|
|
|
|
state = engine.get_electrical_state()
|
|
assert state.input_voltage == 5.0
|
|
|
|
def test_set_load_current(self) -> None:
|
|
"""Test set_load_current updates current."""
|
|
engine = PhysicsEngine()
|
|
engine.set_output_enabled(True)
|
|
|
|
engine.set_load_current(0.1)
|
|
|
|
state = engine.get_electrical_state()
|
|
assert state.load_current == 0.1
|
|
|
|
def test_output_disabled_by_default(self) -> None:
|
|
"""Test output is disabled by default."""
|
|
engine = PhysicsEngine()
|
|
|
|
assert not engine.is_output_enabled
|
|
|
|
def test_enable_output(self) -> None:
|
|
"""Test set_output_enabled enables output."""
|
|
engine = PhysicsEngine()
|
|
|
|
engine.set_output_enabled(True)
|
|
|
|
assert engine.is_output_enabled
|
|
|
|
def test_load_current_zero_when_disabled(self) -> None:
|
|
"""Test load current is zero when output disabled."""
|
|
engine = PhysicsEngine()
|
|
engine.set_load_current(0.1)
|
|
|
|
state = engine.get_electrical_state()
|
|
|
|
assert state.load_current == 0.0 # Disabled, so zero
|