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()