"""Tests for mixer parameter registry.""" import pytest from src.mixer import ParameterRegistry from src.midi.types import ParameterType, ParameterCategory class TestParameterRegistry: @pytest.fixture def registry(self): return ParameterRegistry(channels=8) def test_all_parameters_built(self, registry): """Registry should have all params (master + channels + fx + transport + utility).""" params = list(registry.iter_all()) # Master: 5, FX: 4, Transport: 6, Utility: 4, 8 channels * 25 = 200 # Total: 5 + 4 + 6 + 4 + 200 = 219 assert len(params) == 219 def test_get_master_volume(self, registry): p = registry.get(ParameterType.MASTER_VOLUME) assert p is not None assert p.param_type == ParameterType.MASTER_VOLUME assert p.channel == -1 assert p.max_val == 12.0 def test_get_channel_volume(self, registry): p = registry.get(ParameterType.VOLUME, channel=3) assert p is not None assert p.param_type == ParameterType.VOLUME assert p.channel == 3 assert p.min_val == -60.0 def test_get_nonexistent(self, registry): p = registry.get(ParameterType.VOLUME, channel=999) assert p is None def test_iter_by_category(self, registry): channel_params = list(registry.iter_by_category(ParameterCategory.CHANNEL)) # 8 channels * 25 params = 200 assert len(channel_params) == 8 * 25 master_params = list(registry.iter_by_category(ParameterCategory.MASTER)) assert len(master_params) == 5 def test_iter_by_channel(self, registry): ch0 = list(registry.iter_by_channel(0)) assert len(ch0) == 25 # Full channel strip ch5 = list(registry.iter_by_channel(5)) assert len(ch5) == 25 def test_set_value_and_callback(self, registry): values = [] registry.subscribe(lambda param, value: values.append((param.param_type, value))) registry.set_value(ParameterType.VOLUME, 2, 0.75) assert len(values) == 1 assert values[0][0] == ParameterType.VOLUME assert values[0][1] == 0.75 def test_unsubscribe(self, registry): values = [] def cb(param, value): values.append(value) registry.subscribe(cb) registry.set_value(ParameterType.VOLUME, 0, 0.5) assert len(values) == 1 registry.unsubscribe(cb) registry.set_value(ParameterType.VOLUME, 0, 0.8) assert len(values) == 1 # Not called after unsubscribe def test_set_nonexistent_parameter_no_error(self, registry): """Setting a parameter that doesn't exist should not crash.""" registry.set_value(ParameterType.VOLUME, 999, 0.5) # No error def test_broken_callback_doesnt_block_others(self, registry): def broken(param, value): raise RuntimeError("broken") good_values = [] def good(param, value): good_values.append(value) registry.subscribe(broken) registry.subscribe(good) registry.set_value(ParameterType.VOLUME, 0, 0.5) assert len(good_values) == 1 assert good_values[0] == 0.5 def test_stereo_pan_parameters(self, registry): p = registry.get(ParameterType.PAN, channel=0) assert p is not None assert p.min_val == -1.0 assert p.max_val == 1.0 assert p.default_val == 0.0 def test_mute_is_boolean(self, registry): p = registry.get(ParameterType.MUTE, channel=0) assert p is not None assert p.min_val == 0.0 assert p.max_val == 1.0 assert p.step == 1.0 # Boolean-like def test_transport_params(self, registry): play = registry.get(ParameterType.PLAY) stop = registry.get(ParameterType.STOP) assert play is not None assert stop is not None assert play.step == 1.0 # Triggered