testsAndMisc-archive/python_pkg/brightness_controller/tests/test_brightness_controller_part2.py

233 lines
8.3 KiB
Python
Raw Normal View History

"""Tests for brightness_controller module - part 2 (poll + main)."""
from __future__ import annotations
from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest
from python_pkg.brightness_controller import brightness_controller
MOD = "python_pkg.brightness_controller.brightness_controller"
def _make_controller(
devices: list[brightness_controller.Device] | None = None,
als_path: Path | None = None,
*,
daemon_state: bool = False,
) -> brightness_controller.BrightnessController:
"""Create a BrightnessController with all Tk operations mocked."""
if devices is None:
devices = [
brightness_controller.Device(
"intel_backlight", "backlight", 50, "50%", 120000
)
]
with (
patch(f"{MOD}._get_devices", return_value=devices),
patch(f"{MOD}._find_als_device", return_value=als_path),
patch.object(
brightness_controller.BrightnessController,
"_read_daemon_state",
return_value=daemon_state,
),
patch(f"{MOD}.tk.Tk") as mock_tk,
patch(f"{MOD}.tk.StringVar") as mock_str_var,
patch(f"{MOD}.tk.IntVar") as mock_int_var,
patch(f"{MOD}.ttk"),
patch(f"{MOD}._get_brightness", return_value=50),
):
mock_root = MagicMock()
mock_tk.return_value = mock_root
mock_root.after = MagicMock()
mock_str_var.return_value = MagicMock()
mock_int_var.return_value = MagicMock()
return brightness_controller.BrightnessController()
# ── _sync_auto_ui ────────────────────────────────────────────────────
class TestSyncAutoUi:
"""Tests for _sync_auto_ui."""
def test_no_als_returns_early(self) -> None:
ctrl = _make_controller(als_path=None)
ctrl.als_path = None
ctrl.auto_btn_var = MagicMock()
ctrl.slider = MagicMock()
ctrl._sync_auto_ui()
ctrl.auto_btn_var.set.assert_not_called()
def test_auto_on(self) -> None:
ctrl = _make_controller(als_path=Path("/fake"))
ctrl.auto_mode = True
ctrl.auto_btn_var = MagicMock()
ctrl.slider = MagicMock()
ctrl._sync_auto_ui()
ctrl.auto_btn_var.set.assert_called_once()
assert "ON" in ctrl.auto_btn_var.set.call_args[0][0]
ctrl.slider.state.assert_called_once_with(["disabled"])
def test_auto_off(self) -> None:
ctrl = _make_controller(als_path=Path("/fake"))
ctrl.auto_mode = False
ctrl.auto_btn_var = MagicMock()
ctrl.slider = MagicMock()
ctrl._sync_auto_ui()
ctrl.auto_btn_var.set.assert_called_once()
assert "OFF" in ctrl.auto_btn_var.set.call_args[0][0]
ctrl.slider.state.assert_called_once_with(["!disabled"])
# ── _poll_als ────────────────────────────────────────────────────────
class TestPollAls:
"""Tests for _poll_als."""
@patch(f"{MOD}._read_lux", return_value=42.5)
def test_updates_lux_display(self, _mock_lux: MagicMock) -> None:
ctrl = _make_controller(als_path=Path("/fake"))
ctrl.lux_var = MagicMock()
ctrl.root = MagicMock()
with patch.object(
brightness_controller.BrightnessController,
"_read_daemon_state",
return_value=False,
):
ctrl._poll_als()
assert "42.5 lux" in ctrl.lux_var.set.call_args[0][0]
ctrl.root.after.assert_called_once()
@patch(f"{MOD}._read_lux", side_effect=OSError("sensor fail"))
def test_sensor_error(self, _mock_lux: MagicMock) -> None:
ctrl = _make_controller(als_path=Path("/fake"))
ctrl.lux_var = MagicMock()
ctrl.root = MagicMock()
with patch.object(
brightness_controller.BrightnessController,
"_read_daemon_state",
return_value=False,
):
ctrl._poll_als()
ctrl.lux_var.set.assert_called_with("sensor error")
@patch(f"{MOD}._read_lux", side_effect=ValueError("bad value"))
def test_sensor_value_error(self, _mock_lux: MagicMock) -> None:
ctrl = _make_controller(als_path=Path("/fake"))
ctrl.lux_var = MagicMock()
ctrl.root = MagicMock()
with patch.object(
brightness_controller.BrightnessController,
"_read_daemon_state",
return_value=False,
):
ctrl._poll_als()
ctrl.lux_var.set.assert_called_with("sensor error")
@patch(f"{MOD}._read_lux", return_value=10.0)
def test_syncs_daemon_state_change(self, _mock_lux: MagicMock) -> None:
"""When daemon state differs from auto_mode, syncs it."""
ctrl = _make_controller(als_path=Path("/fake"))
ctrl.auto_mode = False
ctrl.lux_var = MagicMock()
ctrl.auto_btn_var = MagicMock()
ctrl.slider = MagicMock()
ctrl.root = MagicMock()
with patch.object(
brightness_controller.BrightnessController,
"_read_daemon_state",
return_value=True,
):
ctrl._poll_als()
assert ctrl.auto_mode is True
@patch(f"{MOD}._read_lux", return_value=10.0)
def test_no_sync_when_same(self, _mock_lux: MagicMock) -> None:
"""When daemon state matches auto_mode, no sync needed."""
ctrl = _make_controller(als_path=Path("/fake"))
ctrl.auto_mode = False
ctrl.lux_var = MagicMock()
ctrl.root = MagicMock()
with patch.object(
brightness_controller.BrightnessController,
"_read_daemon_state",
return_value=False,
):
ctrl._poll_als()
# No assertion on auto_btn_var since auto_mode didn't change
def test_no_als_path(self) -> None:
ctrl = _make_controller(als_path=None)
ctrl.als_path = None
ctrl.root = MagicMock()
ctrl._poll_als()
ctrl.root.after.assert_called_once()
# ── _poll_brightness ─────────────────────────────────────────────────
class TestPollBrightness:
"""Tests for _poll_brightness."""
@patch(f"{MOD}._get_brightness", return_value=60)
def test_refreshes_when_not_auto(self, _mock_get: MagicMock) -> None:
ctrl = _make_controller()
ctrl.auto_mode = False
ctrl.pct_var = MagicMock()
ctrl.slider_var = MagicMock()
ctrl.root = MagicMock()
ctrl._poll_brightness()
ctrl.pct_var.set.assert_called_with("60%")
ctrl.root.after.assert_called_once()
def test_skips_refresh_when_auto(self) -> None:
ctrl = _make_controller()
ctrl.auto_mode = True
ctrl._refresh_brightness = MagicMock()
ctrl.root = MagicMock()
ctrl._poll_brightness()
ctrl._refresh_brightness.assert_not_called()
ctrl.root.after.assert_called_once()
# ── run ──────────────────────────────────────────────────────────────
class TestRun:
"""Tests for run method."""
def test_calls_mainloop(self) -> None:
ctrl = _make_controller()
ctrl.root = MagicMock()
ctrl.run()
ctrl.root.mainloop.assert_called_once()
# ── main ─────────────────────────────────────────────────────────────
class TestMain:
"""Tests for main() entry point."""
@patch(f"{MOD}.subprocess.run")
def test_brightnessctl_not_found(self, mock_run: MagicMock) -> None:
mock_run.side_effect = FileNotFoundError
with pytest.raises(SystemExit, match="1"):
brightness_controller.main()
@patch(f"{MOD}.BrightnessController")
@patch(f"{MOD}.subprocess.run")
def test_success(self, mock_run: MagicMock, mock_ctrl_cls: MagicMock) -> None:
mock_run.return_value = MagicMock()
mock_app = MagicMock()
mock_ctrl_cls.return_value = mock_app
brightness_controller.main()
mock_app.run.assert_called_once()