NuCon/test/test_core.py
Dominik Roth 90616dcf69 Full parameter coverage compatible with game V2.2.25.213
- Add ~300 missing parameters with types, ranges, and units
- Add SPECIAL_VARIABLES frozenset to block non-param game endpoints
- Fix batch query to handle {"values": {...}} wrapper
- Fix str-typed params falling back to individual GET (batch returns int codes)
- Handle null/empty values from uninstalled subsystems
- Add is_readable field to NuconParameter for write-only support
- Add 57 write-only parameters: SCRAM, emergency stop, bay hatches/fuel loading,
  RODS_ALL_POS_ORDERED, MSCVs, steam turbine bypass/trip, ejector valves,
  VALVE_OPEN/CLOSE/OFF, chemistry rates, FUN_* event triggers
- Update get_all/get_all_readable/get_all_iter to skip write-only params
- __len__ now reflects readable param count (consistent with get_all)
- Update tests to skip write-only params in write test, handle None values

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 16:27:57 +01:00

86 lines
4.1 KiB
Python

import pytest
import warnings
from nucon import Nucon, PumpStatus, PumpDryStatus, PumpOverloadStatus, BreakerStatus
WARN_FLOAT_COULD_BE_INT = False
@pytest.fixture(scope="function")
def nucon():
"""Create a fresh Nucon instance for each test"""
return Nucon()
def test_read_all_parameters(nucon):
all_params = nucon.get_all()
assert len(all_params) == len(nucon)
for param, value in all_params.items():
param_type = nucon.get_type(param)
if value is None:
continue # Some params return null/empty when subsystem not installed
assert isinstance(value, param_type), f"Parameter {param} has incorrect type. Expected {param_type}, got {type(value)}"
if param_type == float and value.is_integer() and WARN_FLOAT_COULD_BE_INT:
warnings.warn(f"Parameter {param} is a float but has an integer value: {value}")
def test_write_writable_parameters(nucon):
writable_params = nucon.get_all_writable()
for param in writable_params.values():
if not param.is_readable:
continue # Skip write-only params (can't read back to verify, and actions like SCRAM are dangerous to trigger)
current_value = param.value
if current_value is None:
continue # Skip params that return null (subsystem not installed)
param.value = current_value
assert param.value == current_value, f"Failed to write to parameter {param.id}"
def test_non_writable_parameters(nucon):
non_writable_params = [param for param in nucon.get_all_readable().values() if not param.is_writable]
for param in non_writable_params:
# Test that normal set raises an error
with pytest.raises(ValueError, match=f"Parameter {param.id} is not writable"):
param.value = param.value # Attempt to write the current value
# Note: the game accepts force writes silently (does not return an error)
def test_enum_parameters(nucon):
pump_status = nucon.COOLANT_CORE_CIRCULATION_PUMP_0_STATUS.value
assert isinstance(pump_status, PumpStatus)
dry_status = nucon.COOLANT_CORE_CIRCULATION_PUMP_0_DRY_STATUS.value
assert isinstance(dry_status, PumpDryStatus)
overload_status = nucon.COOLANT_CORE_CIRCULATION_PUMP_0_OVERLOAD_STATUS.value
assert isinstance(overload_status, PumpOverloadStatus)
breaker_status = nucon.GENERATOR_0_BREAKER.value
assert isinstance(breaker_status, BreakerStatus)
def test_custom_truthy_values(nucon):
assert bool(nucon.COOLANT_CORE_CIRCULATION_PUMP_0_STATUS.value) == (nucon.COOLANT_CORE_CIRCULATION_PUMP_0_STATUS.value in [PumpStatus.ACTIVE_NO_SPEED_REACHED, PumpStatus.ACTIVE_SPEED_REACHED])
#assert bool(nucon.COOLANT_CORE_CIRCULATION_PUMP_0_DRY_STATUS.value) == (nucon.COOLANT_CORE_CIRCULATION_PUMP_0_DRY_STATUS.value == PumpDryStatus.INACTIVE_OR_ACTIVE_WITH_FLUID)
#assert bool(nucon.COOLANT_CORE_CIRCULATION_PUMP_0_OVERLOAD_STATUS.value) == (nucon.COOLANT_CORE_CIRCULATION_PUMP_0_OVERLOAD_STATUS.value == PumpOverloadStatus.INACTIVE_OR_ACTIVE_NO_OVERLOAD)
assert bool(nucon.GENERATOR_0_BREAKER.value) == (nucon.GENERATOR_0_BREAKER.value == BreakerStatus.OPEN)
def test_get_multiple_parameters(nucon):
params_to_get = [nucon.CORE_TEMP, nucon.CORE_PRESSURE, nucon.TIME]
multiple_params = nucon.get_multiple(params_to_get)
assert len(multiple_params) == len(params_to_get)
for param, value in multiple_params.items():
param_type = nucon.get_type(param)
assert isinstance(value, param_type)
def test_param_coverage(nucon):
raw_game_vars = nucon.get_game_variable_names()
game_vars = set(v.upper() for v in raw_game_vars)
our_vars = set(nucon._parameters.keys())
assert game_vars, f"Got empty variable list from game. Raw sample: {raw_game_vars[:5]}"
missing_from_game = our_vars - game_vars
assert not missing_from_game, f"Params defined in NuCon but not found in game: {missing_from_game}\nGame var sample: {sorted(game_vars)[:10]}"
not_in_nucon = game_vars - our_vars
if not_in_nucon:
warnings.warn(f"Game exposes {len(not_in_nucon)} params not defined in NuCon: {sorted(not_in_nucon)}")
if __name__ == "__main__":
pytest.main()