From feab2e0bdc8243af93b84e9fa053c4b1078d996c Mon Sep 17 00:00:00 2001 From: Kai Chappell Date: Tue, 2 Dec 2025 02:38:50 +0000 Subject: [PATCH] Add physics model unit tests Test dataclass creation, immutability, equality, and hashability for ThermalState and ElectricalState. Also test PhysicsEngine stub methods. --- tests/unit/test_physics_models.py | 214 ++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 tests/unit/test_physics_models.py diff --git a/tests/unit/test_physics_models.py b/tests/unit/test_physics_models.py new file mode 100644 index 0000000..3c6d5ac --- /dev/null +++ b/tests/unit/test_physics_models.py @@ -0,0 +1,214 @@ +"""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