Applies gradients derived from 103 bundled Philips Hue scenes to OpenRGB-controlled LEDs, with six mapping modes (sequence, per-device, per-zone, matrix, mirror, shuffle) and five animation modes (static, scroll, pingpong, pulse, wave). Typer/Rich CLI with dry-run preview, device/zone targeting, and a scenes-update command to refresh the bundled dataset from its source gist. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
93 lines
2.5 KiB
Python
93 lines
2.5 KiB
Python
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from openrgb_hue.scenes import (
|
|
BUNDLED_CSV_PATH,
|
|
SceneNotFoundError,
|
|
get_scene,
|
|
list_scene_names,
|
|
load_scenes,
|
|
refresh_bundled_csv,
|
|
)
|
|
|
|
SAMPLE_CSV = (
|
|
"Scene,Light,Red,Green,Blue,Brightness\n"
|
|
"Alpha,light.hue_1,255,0,0,255\n"
|
|
"Alpha,light.hue_2,0,255,0,200\n"
|
|
"Beta,light.hue_1,0,0,255,100\n"
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_csv_path(tmp_path):
|
|
path = tmp_path / "scenes.csv"
|
|
path.write_text(SAMPLE_CSV, encoding="utf-8")
|
|
return path
|
|
|
|
|
|
def test_load_scenes_groups_by_name_preserving_order(sample_csv_path):
|
|
scenes = load_scenes(sample_csv_path)
|
|
assert list(scenes.keys()) == ["Alpha", "Beta"]
|
|
assert len(scenes["Alpha"].lights) == 2
|
|
assert len(scenes["Beta"].lights) == 1
|
|
|
|
|
|
def test_list_scene_names(sample_csv_path):
|
|
assert list_scene_names(sample_csv_path) == ["Alpha", "Beta"]
|
|
|
|
|
|
def test_get_scene_case_insensitive(sample_csv_path):
|
|
scene = get_scene("alpha", sample_csv_path)
|
|
assert scene.name == "Alpha"
|
|
|
|
|
|
def test_get_scene_missing_raises_with_suggestion(sample_csv_path):
|
|
with pytest.raises(SceneNotFoundError, match="Alpha"):
|
|
get_scene("Alphaa", sample_csv_path)
|
|
|
|
|
|
def test_bundled_csv_has_103_scenes_of_10_lights_each():
|
|
scenes = load_scenes(BUNDLED_CSV_PATH)
|
|
assert len(scenes) == 103
|
|
assert all(len(scene.lights) == 10 for scene in scenes.values())
|
|
|
|
|
|
def test_refresh_bundled_csv_writes_validated_data(tmp_path):
|
|
dest = tmp_path / "scenes.csv"
|
|
|
|
class FakeResponse:
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, *exc):
|
|
return False
|
|
|
|
def read(self):
|
|
return SAMPLE_CSV.encode("utf-8")
|
|
|
|
with patch("openrgb_hue.scenes.urllib.request.urlopen", return_value=FakeResponse()):
|
|
result = refresh_bundled_csv(dest=dest, url="https://example.invalid/scenes.csv")
|
|
|
|
assert result == dest
|
|
assert dest.read_text(encoding="utf-8") == SAMPLE_CSV
|
|
|
|
|
|
def test_refresh_bundled_csv_rejects_invalid_data(tmp_path):
|
|
dest = tmp_path / "scenes.csv"
|
|
|
|
class FakeResponse:
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, *exc):
|
|
return False
|
|
|
|
def read(self):
|
|
return b"not,a,valid,csv\n"
|
|
|
|
with patch("openrgb_hue.scenes.urllib.request.urlopen", return_value=FakeResponse()):
|
|
with pytest.raises(ValueError):
|
|
refresh_bundled_csv(dest=dest, url="https://example.invalid/scenes.csv")
|
|
assert not dest.exists()
|