import pytest from openrgb_hue.color import Color from openrgb_hue.gradient import Gradient from openrgb_hue.mapping import ( FrameParams, MatrixMapping, MirrorMapping, PerDeviceMapping, PerZoneMapping, SequenceMapping, ShuffleMapping, UnknownMappingModeError, base_positions, build_mapping, render_frame, ) from openrgb_hue.targets import synthetic_targets def test_sequence_mapping_linear_spacing(synthetic_leds): positions = SequenceMapping().positions(synthetic_leds) ordered = [positions[led] for led in synthetic_leds] assert ordered[0] == 0.0 assert ordered[-1] == 1.0 assert ordered == sorted(ordered) def test_sequence_mapping_single_target_is_zero(): leds = synthetic_targets(1) positions = SequenceMapping().positions(leds) assert positions[leds[0]] == 0.0 def test_per_device_mapping_each_device_spans_full_range(): leds = synthetic_targets() # two linear devices + one matrix device positions = PerDeviceMapping().positions(leds) for device_index in {led.device_index for led in leds}: device_leds = [led for led in leds if led.device_index == device_index] values = [positions[led] for led in device_leds] assert min(values) == 0.0 assert max(values) == 1.0 def test_per_zone_mapping_groups_by_zone(): leds = synthetic_targets() positions = PerZoneMapping().positions(leds) for key in {(led.device_index, led.zone_index) for led in leds}: zone_leds = [led for led in leds if (led.device_index, led.zone_index) == key] values = [positions[led] for led in zone_leds] assert min(values) == 0.0 assert max(values) == 1.0 @pytest.fixture def matrix_leds(): return [led for led in synthetic_targets() if led.matrix_row is not None] def test_matrix_mapping_left_right(matrix_leds): positions = MatrixMapping(direction="left-right").positions(matrix_leds) top_left = next(led for led in matrix_leds if led.matrix_row == 0 and led.matrix_col == 0) top_right = next(led for led in matrix_leds if led.matrix_row == 0 and led.matrix_col == 3) assert positions[top_left] == 0.0 assert positions[top_right] == 1.0 def test_matrix_mapping_top_bottom(matrix_leds): positions = MatrixMapping(direction="top-bottom").positions(matrix_leds) top = next(led for led in matrix_leds if led.matrix_row == 0 and led.matrix_col == 0) bottom = next(led for led in matrix_leds if led.matrix_row == 3 and led.matrix_col == 0) assert positions[top] == 0.0 assert positions[bottom] == 1.0 def test_matrix_mapping_diagonal(matrix_leds): positions = MatrixMapping(direction="diagonal").positions(matrix_leds) corner_near = next(led for led in matrix_leds if led.matrix_row == 0 and led.matrix_col == 0) corner_far = next(led for led in matrix_leds if led.matrix_row == 3 and led.matrix_col == 3) assert positions[corner_near] == 0.0 assert positions[corner_far] == 1.0 def test_matrix_mapping_radial_center_is_minimum(matrix_leds): positions = MatrixMapping(direction="radial").positions(matrix_leds) # On a 4x4 grid the center falls between cells; corners should be # further from center than the near-center cells. corner = next(led for led in matrix_leds if led.matrix_row == 0 and led.matrix_col == 0) near_center = next(led for led in matrix_leds if led.matrix_row == 1 and led.matrix_col == 1) assert positions[near_center] < positions[corner] def test_matrix_mapping_invalid_direction_raises(): with pytest.raises(ValueError): MatrixMapping(direction="sideways") def test_matrix_mapping_warns_on_non_matrix_leds(): leds = synthetic_targets(4) # flat LEDs, no matrix coordinates warnings = [] MatrixMapping(warn=warnings.append).positions(leds) assert len(warnings) == 1 assert all(led.matrix_row is None for led in leds) def test_mirror_mapping_is_symmetric(synthetic_leds): positions = MirrorMapping().positions(synthetic_leds) values = [positions[led] for led in synthetic_leds] n = len(values) for i in range(n): assert values[i] == pytest.approx(values[n - 1 - i]) def test_shuffle_mapping_deterministic_and_seed_sensitive(synthetic_leds): a = ShuffleMapping(seed=1).positions(synthetic_leds) b = ShuffleMapping(seed=1).positions(synthetic_leds) c = ShuffleMapping(seed=2).positions(synthetic_leds) assert a == b assert a != c def test_build_mapping_unknown_name_raises(): with pytest.raises(UnknownMappingModeError): build_mapping("not-a-real-mode") def test_render_frame_uses_gradient_and_offset(synthetic_leds): gradient = Gradient.from_colors([Color(255, 0, 0), Color(0, 0, 255)], interpolation="rgb") base = base_positions(SequenceMapping(), synthetic_leds) colors = render_frame(base, gradient, FrameParams(t_offset=0.0, brightness=1.0)) assert colors[synthetic_leds[0]] == gradient.sample(0.0) def test_render_frame_brightness_fn_overrides_scalar_brightness(synthetic_leds): gradient = Gradient.from_colors([Color(255, 255, 255)], interpolation="rgb") base = base_positions(SequenceMapping(), synthetic_leds) params = FrameParams(brightness=1.0, brightness_fn=lambda t: 0.0) colors = render_frame(base, gradient, params) assert all(c == Color(0, 0, 0) for c in colors.values())