testsAndMisc-archive/python_pkg/brightness_controller/tests/test_brightness_controller_part2.py
Krzysztof kuhy Rudnicki e5fd82c822 Reduce per-file-ignores by fixing lint violations across codebase
Fix ruff violations in ~15 source files and ~60+ test files to minimize
per-file-ignores in pyproject.toml. Remaining ignores are justified with
comments explaining why each suppression is necessary.

Source fixes: FBT003 (keyword args), S310 (URL validation), SLF001
(private access), T201 (print→logging), C901 (complexity), E501 (line
length), E402 (import order).

Test fixes: SIM117 (combined with), FBT (boolean args), PERF203 (try in
loop), S310/S607 (URLs/executables), E402/E501 (imports/lines), S108
(tmp paths), PLR0913 (too many args), ARG (unused args), ANN (type
annotations), RUF059 (unused unpacked vars), PT019 (fixture naming).

Remaining per-file-ignores (with justifications):
- Tests: ARG, D, PLC0415, PLR2004, S101, SLF001
- music_gen sources: PLC0415 (heavy ML lazy imports)
- moviepy_showcase: PLC0415 (circular dependency)
- generate_images: PLR0913 (matplotlib helpers need many params)
- praca_magisterska_video: E501, E402 (long paths, mpl.use)
2026-03-25 18:58:05 +01:00

233 lines
8.3 KiB
Python

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